clean up and refactor half-baked development commits and squash them into a new version.

Changes:

* support for multiple GTFS feeds as input in filtering, read default global and local configuration files
* be a bit more memory conservative
* make caching optional
* dont delete orphan edge if it would transform a degree 3 node with a possible full turn into a degree 2 node eligible for contraction
* dedicated filters for funicular and gondola
* make max snap level option more intuitive
* allow filter rules for level 0
* additional fallback for station snapping
* dont try to route for MOT unequal to trip in -T mode, force-snap to orphaned OSM station if not snap was possible
* write bounds to filtered osm
* remove unused surrounding heuristic
* use bus lanes info
* be a bit more tolerant for bus oneway streets
* create missing directories
* error if no cfg is present, clean up evaluation Makefile
This commit is contained in:
Patrick Brosi 2019-01-10 16:52:59 +01:00
parent 2cc2d2dc23
commit 63f0b61ea1
60 changed files with 4532 additions and 1576 deletions

View file

@ -25,4 +25,6 @@
#define BOX util::geo::Box<PFAEDLE_PRECISION>
#define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION>
#define BOX_PADDING 2500
#endif // PFAEDLE_DEF_H_

View file

@ -2,19 +2,25 @@
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.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/gtfs/Feed.h"
#include "pfaedle/gtfs/Writer.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/router/ShapeBuilder.h"
@ -30,13 +36,28 @@ using pfaedle::osm::OsmBuilder;
using pfaedle::config::MotConfig;
using pfaedle::config::Config;
using pfaedle::router::ShapeBuilder;
using configparser::ParseFileExc;
using pfaedle::config::MotConfigReader;
using pfaedle::config::ConfigReader;
using pfaedle::eval::Collector;
enum class RetCode {
SUCCESS = 0,
NO_INPUT_FEED = 1,
MULT_FEEDS_NOT_ALWD = 2,
TRIP_NOT_FOUND = 3,
GTFS_PARSE_ERR = 4,
NO_OSM_INPUT = 5,
MOT_CFG_PARSE_ERR = 6,
OSM_PARSE_ERR = 7,
GTFS_WRITE_ERR = 8,
NO_MOT_CFG = 9
};
std::string getMotStr(const MOTs& mots);
std::string getFileNameMotStr(const MOTs& mots);
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots);
std::vector<std::string> getCfgPaths(const Config& cfg);
// _____________________________________________________________________________
int main(int argc, char** argv) {
@ -52,44 +73,90 @@ int main(int argc, char** argv) {
ConfigReader cr;
cr.read(&cfg, argc, argv);
ad::cppgtfs::gtfs::Feed gtfs;
std::vector<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
// feed containing the shapeas in memory for evaluation
ad::cppgtfs::gtfs::Feed evalFeed;
motCfgReader.parse(cfg.configPaths);
std::vector<std::string> cfgPaths = getCfgPaths(cfg);
if (cfg.osmPath.empty()) {
try {
motCfgReader.parse(cfgPaths);
} catch (configparser::ParseExc ex) {
LOG(ERROR) << "Could not parse MOT configurations, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::MOT_CFG_PARSE_ERR));
}
if (cfg.osmPath.empty() && !cfg.writeOverpass) {
std::cerr << "No OSM input file specified (-x), see --help." << std::endl;
exit(5);
exit(static_cast<int>(RetCode::NO_OSM_INPUT));
}
if (motCfgReader.getConfigs().size() == 0) {
LOG(ERROR) << "No MOT configurations specified and no implicit "
"configurations found, see --help.";
exit(static_cast<int>(RetCode::NO_MOT_CFG));
}
if (cfg.feedPaths.size() == 1) {
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
ad::cppgtfs::Parser p;
p.parse(&gtfs, cfg.feedPaths[0]);
LOG(INFO) << "Done.";
if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0];
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
try {
ad::cppgtfs::Parser p;
p.parse(&gtfs[0], cfg.feedPaths[0]);
if (cfg.evaluate) {
// read the shapes and store them in memory
p.parseShapes(&evalFeed, cfg.feedPaths[0]);
}
} catch (ad::cppgtfs::ParserException ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
} else if (cfg.writeOsm.size() || cfg.writeOverpass) {
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[i] << " ...";
ad::cppgtfs::Parser p;
try {
p.parse(&gtfs[i], cfg.feedPaths[i]);
} catch (ad::cppgtfs::ParserException ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
}
} else if (cfg.feedPaths.size() > 1) {
std::cerr << "Maximally one input feed allowed." << std::endl;
exit(2);
std::cerr << "Multiple feeds only allowed in filter mode." << std::endl;
exit(static_cast<int>(RetCode::MULT_FEEDS_NOT_ALWD));
}
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
<< " unique MOT configs.";
MOTs cmdCfgMots = cfg.mots;
ad::cppgtfs::gtfs::Trip* singleTrip = 0;
pfaedle::gtfs::Trip* singleTrip = 0;
if (cfg.shapeTripId.size()) {
singleTrip = gtfs.getTrips().get(cfg.shapeTripId);
if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl;
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
singleTrip = gtfs[0].getTrips().get(cfg.shapeTripId);
if (!singleTrip) {
LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found.";
exit(3);
exit(static_cast<int>(RetCode::TRIP_NOT_FOUND));
}
}
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, true);
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -99,15 +166,33 @@ int main(int argc, char** argv) {
opts.push_back(o.osmBuildOpts);
}
}
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
exit(0);
try {
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
} catch (xml::XmlFileException ex) {
LOG(ERROR) << "Could not parse OSM data, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::OSM_PARSE_ERR));
}
exit(static_cast<int>(RetCode::SUCCESS));
} else if (cfg.writeOverpass) {
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box);
}
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.overpassQryWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl;
exit(1);
}
if (motCfgReader.getConfigs().size() == 0) {
LOG(WARN) << "No MOT configurations specified, see --help.";
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
std::vector<double> dfBins;
@ -119,62 +204,76 @@ int main(int argc, char** argv) {
std::string filePost;
auto usedMots = getContMots(motCfg, cmdCfgMots);
if (!usedMots.size()) continue;
if (singleTrip && !usedMots.count(singleTrip->getRoute()->getType()))
continue;
if (motCfgReader.getConfigs().size() > 1)
filePost = getFileNameMotStr(usedMots);
std::string motStr = getMotStr(usedMots);
LOG(INFO) << "Calculating shapes for mots " << motStr;
ShapeBuilder shapeBuilder(&gtfs, cmdCfgMots, motCfg, &ecoll, cfg);
try {
ShapeBuilder shapeBuilder(&gtfs[0], &evalFeed, 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, util::json::Dict{{"ver", "old"}});
if (cfg.writeGraph) {
LOG(INFO) << "Outputting graph.json...";
util::geo::output::GeoGraphJsonOutput out;
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
out.print(*shapeBuilder.getGraph(), fstr);
fstr.close();
}
o.print(l, util::json::Dict{{"ver", "new"}});
o.flush();
pstr.close();
if (singleTrip) {
LOG(INFO) << "Outputting path.json...";
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
exit(0);
}
auto l = shapeBuilder.shapeL(singleTrip);
pfaedle::netgraph::Graph ng;
shapeBuilder.shape(&ng);
o.print(l, util::json::Dict{{"ver", "new"}});
o.flush();
pstr.close();
if (cfg.buildTransitGraph) {
util::geo::output::GeoGraphJsonOutput out;
LOG(INFO) << "Outputting trgraph" + filePost + ".json...";
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph" + filePost + ".json");
out.print(ng, fstr);
fstr.close();
exit(static_cast<int>(RetCode::SUCCESS));
}
pfaedle::netgraph::Graph ng;
shapeBuilder.shape(&ng);
if (cfg.buildTransitGraph) {
util::geo::output::GeoGraphJsonOutput out;
LOG(INFO) << "Outputting trgraph" + filePost + ".json...";
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph" + filePost + ".json");
out.print(ng, fstr);
fstr.close();
}
} catch (xml::XmlFileException ex) {
LOG(ERROR) << "Could not parse OSM data, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::OSM_PARSE_ERR));
}
}
if (cfg.evaluate) ecoll.printStats(&std::cout);
if (cfg.feedPaths.size()) {
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
ad::cppgtfs::Writer w;
w.write(&gtfs, cfg.outputPath);
try {
mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
pfaedle::gtfs::Writer w;
w.write(&gtfs[0], cfg.outputPath);
} catch (ad::cppgtfs::WriterException ex) {
LOG(ERROR) << "Could not write final GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_WRITE_ERR));
}
}
return (0);
return static_cast<int>(RetCode::SUCCESS);
}
// _____________________________________________________________________________
@ -183,7 +282,7 @@ std::string getMotStr(const MOTs& mots) {
std::string motStr;
for (const auto& mot : mots) {
if (first) motStr += ", ";
motStr += "<" + Route::getTypeString(mot) + ">";
motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">";
first = true;
}
@ -194,7 +293,7 @@ std::string getMotStr(const MOTs& mots) {
std::string getFileNameMotStr(const MOTs& mots) {
std::string motStr;
for (const auto& mot : mots) {
motStr += "-" + Route::getTypeString(mot);
motStr += "-" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot);
}
return motStr;
@ -211,3 +310,73 @@ MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) {
return ret;
}
// _____________________________________________________________________________
std::vector<std::string> getCfgPaths(const Config& cfg) {
if (cfg.configPaths.size()) return cfg.configPaths;
std::vector<std::string> ret;
// parse implicit paths
const char* homedir = 0;
char* buf = 0;
if ((homedir = getenv("HOME")) == 0) {
homedir = "";
struct passwd pwd;
struct passwd* result;
size_t bufsize;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == static_cast<size_t>(-1)) bufsize = 0x4000;
buf = static_cast<char*>(malloc(bufsize));
if (buf != 0) {
getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
if (result != NULL) homedir = result->pw_dir;
}
}
// install prefix global configuration path, if available
{
auto path = std::string(INSTALL_PREFIX) +
std::string(XDG_CONFIG_DIRS_DEFAULT) + "/" + "pfaedle" + "/" +
CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
// local user configuration path, if available
{
auto path = std::string(homedir) + XDG_CONFIG_HOME_SUFFIX + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
if (buf) free(buf);
// CWD
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd))) {
auto path = std::string(cwd) + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
}
return ret;
}

View file

@ -7,4 +7,7 @@
// version number from cmake version module
#define VERSION_FULL "@VERSION_GIT_FULL@"
// version number from cmake version module
#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
#endif // SRC_PFAEDLE_CONFIG_H_N

View file

@ -26,67 +26,96 @@ static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
// _____________________________________________________________________________
void ConfigReader::help(const char* bin) {
std::cout
<< std::setfill(' ') << std::left << "pfaedle GTFS map matcher\n"
<< VERSION_FULL << " (built " << __DATE__ << " " << __TIME__
<< " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n"
<< "(C) " << YEAR << " " << COPY << "\n"
<< "Authors: " << AUTHORS << "\n\n"
<< "Usage: " << bin << " -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"
<< std::setw(35) << " -D [ --drop-shapes ]"
<< "drop shapes already present in the feed and recalculate them\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"
<< "if -T is set, write combination graph as GeoJSON to "
"<dbg-path>/combgraph.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";
std::cout << std::setfill(' ') << std::left << "pfaedle GTFS map matcher "
<< VERSION_FULL << "\n(built " << __DATE__ << " " << __TIME__
<< " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n"
<< "(C) " << YEAR << " " << COPY << "\n"
<< "Authors: " << AUTHORS << "\n\n"
<< "Usage: " << bin
<< " -x <OSM 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"
<< std::setw(35) << " -D [ --drop-shapes ]"
<< "drop shapes already present in the feed and\n"
<< std::setw(35) << " "
<< " recalculate them\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\n"
<< std::setw(35) << " "
<< " 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 sep.,\n"
<< std::setw(35) << " "
<< " either as string "
"{all, tram | streetcar,\n"
<< std::setw(35) << " "
<< " subway | metro, rail | train, bus,\n"
<< std::setw(35) << " "
<< " ferry | boat | ship, cablecar, gondola,\n"
<< std::setw(35) << " "
<< " funicular, coach} 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\n"
<< std::setw(35) << " "
<< " written to <arg>\n"
<< std::setw(35) << " --inplace"
<< "overwrite input GTFS feed with output feed\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\n"
<< std::setw(35) << " "
<< " <dbg-path>/trgraph.json\n"
<< std::setw(35) << " --write-graph"
<< "write routing graph as GeoJSON to\n"
<< std::setw(35) << " "
<< " <dbg-path>/graph.json\n"
<< std::setw(35) << " --write-cgraph"
<< "if -T is set, write combination graph as\n"
<< std::setw(35) << " "
<< " GeoJSON to "
"<dbg-path>/combgraph.json\n"
<< std::setw(35) << " --method arg (=global)"
<< "matching method to use, either 'global'\n"
<< std::setw(35) << " "
<< " (based on HMM), 'greedy' or "
"'greedy2'\n"
<< std::setw(35) << " --eval"
<< "evaluate existing shapes against matched\n"
<< std::setw(35) << " "
<< " 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 sep.\n"
<< std::setw(35) << " "
<< " (e.g. 10,20,30,40)\n"
<< "\nMisc:\n"
<< std::setw(35) << " -T [ --trip-id ] arg"
<< "Do routing only for trip <arg>, write result \n"
<< std::setw(35) << " "
<< " to <dbg-path>/path.json\n"
<< std::setw(35) << " --overpass"
<< "Output overpass query for matching OSM data\n"
<< std::setw(35) << " --grid-size arg (=2000)"
<< "Grid cell size\n"
<< std::setw(35) << " --use-route-cache"
<< "(experimental) cache intermediate routing\n"
<< std::setw(35) << " "
<< " results\n";
}
// _____________________________________________________________________________
@ -101,6 +130,7 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
{"drop-shapes", required_argument, 0, 'D'},
{"mots", required_argument, NULL, 'm'},
{"grid-size", required_argument, 0, 'g'},
{"overpass", no_argument, 0, 'a'},
{"osm-out", required_argument, 0, 'X'},
{"trip-id", required_argument, 0, 'T'},
{"write-graph", no_argument, 0, 1},
@ -113,6 +143,8 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
{"dbg-path", required_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"inplace", no_argument, 0, 9},
{"use-route-cache", no_argument, 0, 8},
{0, 0, 0, 0}};
char c;
@ -140,6 +172,9 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
case 7:
cfg->evalDfBins = optarg;
break;
case 8:
cfg->useCaching = true;
break;
case 'o':
cfg->outputPath = optarg;
break;
@ -170,6 +205,12 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
case 'd':
cfg->dbgOutputPath = optarg;
break;
case 'a':
cfg->writeOverpass = true;
break;
case 9:
cfg->inPlace = true;
break;
case 'v':
std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " "
<< __TIME__ << " with geometry precision <"
@ -204,7 +245,8 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
auto v = util::split(motStr, ',');
for (const auto& motStr : v) {
const auto& mots = Route::getTypesFromString(util::trim(motStr));
const auto& mots =
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr));
cfg->mots.insert(mots.begin(), mots.end());
}

View file

@ -7,6 +7,7 @@
#include "pfaedle/config/MotConfigReader.h"
#include "util/Misc.h"
#include "util/String.h"
#include "util/log/Log.h"
using pfaedle::config::MotConfigReader;
using pfaedle::config::MotConfig;
@ -23,341 +24,336 @@ MotConfigReader::MotConfigReader() {}
// _____________________________________________________________________________
void MotConfigReader::parse(const std::vector<std::string>& paths) {
ConfigFileParser p;
// parse explicitely given paths
for (const auto& s : paths) {
ConfigFileParser p;
LOG(DEBUG) << "Reading config file " << s;
p.parse(s);
}
for (const auto& sec : p.getSecs()) {
MotConfig curCfg;
std::string secStr = sec.first;
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", ' ')) {
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 < 8; i++) {
std::string name = std::string("osm_filter_lvl") + std::to_string(i);
if (p.hasKey(secStr, name)) {
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.levelFilters[i][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_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_max_snap_level")) {
curCfg.osmBuildOpts.maxSnapLevel =
p.getInt(sec.first, "osm_max_snap_level");
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_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.osmBuildOpts.maxSnapLevel = 7;
curCfg.routingOpts.levelPunish[i] = 1;
}
}
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, "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;
curCfg.osmBuildOpts.fullTurnAngle = ang;
} else {
curCfg.routingOpts.fullTurnAngle = 5;
curCfg.osmBuildOpts.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() + ">",
p.getVal(secStr, "station_normalize_chain").file);
}
}
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, "track_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">",
p.getVal(secStr, "track_normalize_chain").file);
}
}
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() + ">",
p.getVal(secStr, "station_normalize_chain").file);
}
}
bool found = false;
for (auto& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto mot :
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) {
cfg.mots.insert(mot);
}
found = true;
break;
}
}
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);
}
if (!found) {
curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
_cfgs.push_back(curCfg);
}
}
}

View file

@ -5,6 +5,30 @@
#ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#define PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#include "pfaedle/_config.h"
#ifndef HOME_VAR
#define HOME_VAR "HOME"
#endif
#ifndef XDG_DATA_HOME_SUFFIX
#define XDG_DATA_HOME_SUFFIX "/.local/share"
#endif
#ifndef XDG_CONFIG_HOME_SUFFIX
#define XDG_CONFIG_HOME_SUFFIX "/.config"
#endif
#ifndef XDG_CACHE_HOME_SUFFIX
#define XDG_CACHE_HOME_SUFFIX "/.cache"
#endif
#ifndef XDG_DATA_DIRS_DEFAULT
#define XDG_DATA_DIRS_DEFAULT "/usr/local/share"
#endif
#ifndef XDG_CONFIG_DIRS_DEFAULT
#define XDG_CONFIG_DIRS_DEFAULT "/etc"
#endif
#ifndef CFG_FILE_NAME
#define CFG_FILE_NAME "pfaedle.cfg"
#endif
#include <unordered_map>
#include <vector>
#include <set>

View file

@ -28,6 +28,9 @@ struct Config {
writeCombGraph(false),
evaluate(false),
buildTransitGraph(false),
useCaching(false),
writeOverpass(false),
inPlace(false),
gridSize(2000) {}
std::string dbgOutputPath;
std::string solveMethod;
@ -46,6 +49,9 @@ struct Config {
bool writeCombGraph;
bool evaluate;
bool buildTransitGraph;
bool useCaching;
bool writeOverpass;
bool inPlace;
double gridSize;
std::string toString() {
@ -60,6 +66,8 @@ struct Config {
<< "write-graph: " << writeGraph << "\n"
<< "write-cgraph: " << writeCombGraph << "\n"
<< "grid-size: " << gridSize << "\n"
<< "use-cache: " << useCaching << "\n"
<< "write-overpass: " << writeOverpass << "\n"
<< "feed-paths: ";
for (const auto& p : feedPaths) {

View file

@ -18,14 +18,14 @@
using util::geo::PolyLine;
using ad::cppgtfs::gtfs::Trip;
using pfaedle::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,
double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newTripDists) {
if (!oldS) {
_noOrigShp++;
@ -51,7 +51,7 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
std::vector<double> newDists;
LINE newL = getWebMercLine(newS, -1, -1, &newDists);
LINE newL = getWebMercLine(&newS, -1, -1, &newDists);
std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json");
GeoJsonOutput gjout(fstr);
@ -123,19 +123,19 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
6378137.0)) -
1.5707965);
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS)) {
fd = _dCache[oldS][newS];
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS.getId())) {
fd = _dCache[oldS][newS.getId()];
} else {
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
_dCache[oldS][newS] = fd;
_dCache[oldS][newS.getId()] = fd;
}
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS)) {
unmatchedSegments = _dACache[oldS][newS].first;
unmatchedSegmentsLength = _dACache[oldS][newS].second;
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS.getId())) {
unmatchedSegments = _dACache[oldS][newS.getId()].first;
unmatchedSegmentsLength = _dACache[oldS][newS.getId()].second;
} else {
auto dA = getDa(oldSegs, newSegs);
_dACache[oldS][newS] = dA;
_dACache[oldS][newS.getId()] = dA;
unmatchedSegments = dA.first;
unmatchedSegmentsLength = dA.second;
}
@ -199,6 +199,8 @@ std::vector<LINE> Collector::segmentize(
// 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();
if (before + 1 > shape.size()) before = shape.size() - 1;
assert(shape.begin() + before + 1 <= shape.end());
POLYLINE l(LINE(shape.begin(), shape.begin() + before + 1));
auto lastLp = l.projectOn(cuts.front().first);

View file

@ -12,11 +12,12 @@
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h"
using ad::cppgtfs::gtfs::Trip;
using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
namespace pfaedle {
@ -37,7 +38,7 @@ class Collector {
// 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,
double add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newDists);
// Return the set of all Result objects
@ -65,8 +66,8 @@ class Collector {
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> > >
std::map<const Shape*, std::map<std::string, double> > _dCache;
std::map<const Shape*, std::map<std::string, std::pair<size_t, double> > >
_dACache;
size_t _noOrigShp;

View file

@ -5,9 +5,10 @@
#ifndef PFAEDLE_EVAL_RESULT_H_
#define PFAEDLE_EVAL_RESULT_H_
#include "pfaedle/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Feed.h"
using ad::cppgtfs::gtfs::Trip;
using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
namespace pfaedle {

37
src/pfaedle/gtfs/Feed.h Normal file
View file

@ -0,0 +1,37 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_FEED_H_
#define PFAEDLE_GTFS_FEED_H_
#include <string>
#include "Route.h"
#include "Service.h"
#include "ShapeContainer.h"
#include "StopTime.h"
#include "ad/cppgtfs/gtfs/ContContainer.h"
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/NullContainer.h"
#include "ad/cppgtfs/gtfs/Stop.h"
#include "ad/cppgtfs/gtfs/StopTime.h"
#include "ad/cppgtfs/gtfs/Trip.h"
namespace pfaedle {
namespace gtfs {
typedef ad::cppgtfs::gtfs::FeedB<
ad::cppgtfs::gtfs::Agency, Route, ad::cppgtfs::gtfs::Stop, Service,
StopTime, Shape, ad::cppgtfs::gtfs::Fare, ad::cppgtfs::gtfs::Container,
ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::NullContainer,
ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::ContContainer,
ShapeContainer, ad::cppgtfs::gtfs::NullContainer>
Feed;
typedef ad::cppgtfs::gtfs::TripB<StopTime<ad::cppgtfs::gtfs::Stop>, Service,
Route, Shape>
Trip;
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_FEED_H_

61
src/pfaedle/gtfs/Route.h Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_ROUTE_H_
#define PFAEDLE_GTFS_ROUTE_H_
#include <stdint.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <sstream>
#include <string>
#include "ad/cppgtfs/gtfs/Agency.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "util/Misc.h"
using std::exception;
using std::string;
namespace pfaedle {
namespace gtfs {
class Route {
public:
typedef Route* Ref;
static std::string getId(Ref r) { return r->getId(); }
Route() {}
Route(const string& id, ad::cppgtfs::gtfs::Agency* agency,
const string& short_name, const string& long_name, const string& desc,
ad::cppgtfs::gtfs::flat::Route::TYPE type, const string& url,
uint32_t color, uint32_t text_color)
: _id(id), _short_name(short_name), _long_name(long_name), _type(type) {
UNUSED(agency);
UNUSED(desc);
UNUSED(url);
UNUSED(color);
UNUSED(text_color);
}
const std::string& getId() const { return _id; }
const std::string& getShortName() const { return _short_name; }
const std::string& getLongName() const { return _long_name; }
ad::cppgtfs::gtfs::flat::Route::TYPE getType() const { return _type; }
private:
string _id;
string _short_name;
string _long_name;
ad::cppgtfs::gtfs::flat::Route::TYPE _type;
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_ROUTE_H_

View file

@ -0,0 +1,43 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_SERVICE_H_
#define PFAEDLE_GTFS_SERVICE_H_
#include <string>
#include "ad/cppgtfs/gtfs/Service.h"
#include "util/Misc.h"
namespace pfaedle {
namespace gtfs {
class Service {
public:
typedef std::string Ref;
static std::string getId(Ref r) { return r; }
explicit Service(const string& id) : _id(id) {}
Service(const string& id, uint8_t serviceDays,
ad::cppgtfs::gtfs::ServiceDate start,
ad::cppgtfs::gtfs::ServiceDate end)
: _id(id) {
UNUSED(serviceDays);
UNUSED(start);
UNUSED(end);
}
const std::string& getId() const { return _id; }
void addException(const ad::cppgtfs::gtfs::ServiceDate& d,
ad::cppgtfs::gtfs::Service::EXCEPTION_TYPE t) {
UNUSED(d);
UNUSED(t);
}
private:
std::string _id;
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_SERVICE_H_

View file

@ -0,0 +1,69 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_SHAPECONTAINER_H_
#define PFAEDLE_GTFS_SHAPECONTAINER_H_
#include <fstream>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include "ad/cppgtfs/gtfs/Shape.h"
#include "ad/cppgtfs/gtfs/flat/Shape.h"
#include "util/Misc.h"
namespace pfaedle {
namespace gtfs {
struct Shape {
explicit Shape(const std::string& id) : id(id) {}
typedef std::string Ref;
static std::string getId(Ref r) { return r; }
template <typename T>
bool addPoint(T p) {
UNUSED(p);
return true;
}
const std::string& getId() const { return id; }
std::string id;
};
template <typename T>
class ShapeContainer {
public:
ShapeContainer();
~ShapeContainer();
T* add(const T& obj);
bool remove(const std::string& id);
const T* get(const std::string& id) const;
T* get(const std::string& id);
const std::string getRef(const std::string& id) const;
std::string getRef(const std::string& id);
size_t size() const;
void finalize() {}
bool has(const std::string& id) const;
std::string add(const ad::cppgtfs::gtfs::Shape& s);
void open();
bool nextStoragePt(ad::cppgtfs::gtfs::flat::ShapePoint* ret);
private:
std::set<std::string> _ids;
std::fstream _storage;
size_t _ptr;
size_t _max;
std::string _curId;
std::stringstream _writeBuffer;
};
#include "ShapeContainer.tpp"
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_SHAPECONTAINER_H_

View file

@ -0,0 +1,154 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <unistd.h>
#include <string>
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::ShapeContainer() {
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();
}
_storage.open(f, std::fstream::in | std::fstream::out | std::fstream::trunc);
// immediately unlink
unlink(f.c_str());
if (!_storage.good()) {
std::cerr << "Could not open temporary file " << f << std::endl;
exit(1);
}
}
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::~ShapeContainer() {
_storage.close();
}
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::add(const T& ent) {
_ids.insert(ent.getId());
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::remove(const std::string& id) {
_ids.erase(id);
return true;
}
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::get(const std::string& id) {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
const T* ShapeContainer<T>::get(const std::string& id) const {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::has(const std::string& id) const {
return _ids.count(id);
}
// ____________________________________________________________________________
template <typename T>
size_t ShapeContainer<T>::size() const {
return _ids.size();
}
// ____________________________________________________________________________
template <typename T>
std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
if (has(s.getId())) return s.getId();
_ids.insert(s.getId());
_writeBuffer << s.getId() << '\t' << s.getPoints().size();
_writeBuffer << std::setprecision(11);
for (auto p : s.getPoints()) {
_writeBuffer << " " << p.lat << " " << p.lng << " " << p.travelDist;
}
// entries are newline separated
_writeBuffer << '\n';
if (_writeBuffer.tellp() > 1000 * 5000) {
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
}
return s.getId();
}
// ____________________________________________________________________________
template <typename T>
void ShapeContainer<T>::open() {
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
_ptr = 0;
_max = 0;
_storage.clear();
_storage.seekg(0, std::ios::beg);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::nextStoragePt(
ad::cppgtfs::gtfs::flat::ShapePoint* ret) {
while (_storage.good() && !_storage.fail()) {
if (!_ptr) {
_storage >> _curId;
_storage >> _max;
}
if (!_storage.good() || _storage.fail()) return false;
_storage >> ret->lat;
_storage >> ret->lng;
_storage >> ret->travelDist;
ret->seq = _ptr + 1;
ret->id = _curId;
if (_ptr + 1 == _max)
_ptr = 0;
else
_ptr++;
if (!_storage.good() || _storage.fail()) return false;
if (has(ret->id)) return true;
}
return false;
}
// ____________________________________________________________________________
template <typename T>
const std::string ShapeContainer<T>::getRef(const std::string& id) const {
if (!has(id)) return "";
return id;
}
// ____________________________________________________________________________
template <typename T>
std::string ShapeContainer<T>::getRef(const std::string& id) {
if (!has(id)) return "";
return id;
}

View file

@ -0,0 +1,71 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_STOPTIME_H_
#define PFAEDLE_GTFS_STOPTIME_H_
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include "ad/cppgtfs/gtfs/Stop.h"
#include "ad/cppgtfs/gtfs/StopTime.h"
#include "util/Misc.h"
using std::exception;
using std::string;
namespace pfaedle {
namespace gtfs {
template <typename StopT>
class StopTime {
public:
StopTime(const ad::cppgtfs::gtfs::Time& at, const ad::cppgtfs::gtfs::Time& dt,
typename StopT::Ref s, uint32_t seq, const std::string& hs,
ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE put,
ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE dot, float distTrav,
bool isTp)
: _s(s), _sequence(seq), _dist(distTrav) {
UNUSED(at);
UNUSED(dt);
UNUSED(hs);
UNUSED(put);
UNUSED(dot);
UNUSED(distTrav);
UNUSED(isTp);
}
const typename StopT::Ref getStop() const { return _s; }
typename StopT::Ref getStop() { return _s; }
void setShapeDistanceTravelled(double d) { _dist = d; }
ad::cppgtfs::gtfs::Time getArrivalTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
}
ad::cppgtfs::gtfs::Time getDepartureTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
}
float getShapeDistanceTravelled() const { return _dist; }
uint16_t getSeq() const { return _sequence; }
private:
typename StopT::Ref _s;
uint32_t _sequence;
float _dist;
};
template <typename StopTimeT>
struct StopTimeCompare {
bool operator()(const StopTimeT& lh, const StopTimeT& rh) const {
return lh.getSeq() < rh.getSeq();
}
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_STOPTIME_H_

495
src/pfaedle/gtfs/Writer.cpp Normal file
View file

@ -0,0 +1,495 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <cstdio>
#include <fstream>
#include <map>
#include <string>
#include <utility>
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
#include "ad/cppgtfs/gtfs/flat/Agency.h"
#include "ad/util/CsvWriter.h"
#include "pfaedle/gtfs/Writer.h"
using ad::util::CsvWriter;
using ad::cppgtfs::Parser;
using pfaedle::gtfs::Writer;
// ____________________________________________________________________________
bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const {
std::ofstream fs;
std::ifstream is;
std::string gtfsPath(path);
std::string curFile;
std::string curFileTg;
curFile = gtfsPath + "/.agency.txt";
curFileTg = gtfsPath + "/agency.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeAgency(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
curFile = gtfsPath + "/.stops.txt";
curFileTg = gtfsPath + "/stops.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeStops(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
curFile = gtfsPath + "/.routes.txt";
curFileTg = gtfsPath + "/routes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeRoutes(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.open((sourceFeed->getPath() + "/calendar.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.calendar.txt";
curFileTg = gtfsPath + "/calendar.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeCalendar(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.calendar_dates.txt";
curFileTg = gtfsPath + "/calendar_dates.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeCalendarDates(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/transfers.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.transfers.txt";
curFileTg = gtfsPath + "/transfers.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeTransfers(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.fare_attributes.txt";
curFileTg = gtfsPath + "/fare_attributes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFares(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.fare_rules.txt";
curFileTg = gtfsPath + "/fare_rules.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFareRules(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.close();
curFile = gtfsPath + "/.shapes.txt";
curFileTg = gtfsPath + "/shapes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeShapes(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.close();
curFile = gtfsPath + "/.trips.txt";
curFileTg = gtfsPath + "/trips.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
bool hasFreqs = writeTrips(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.open((sourceFeed->getPath() + "/frequencies.txt").c_str());
if (hasFreqs && is.good()) {
is.close();
curFile = gtfsPath + "/.frequencies.txt";
curFileTg = gtfsPath + "/frequencies.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFrequencies(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.close();
curFile = gtfsPath + "/.stop_times.txt";
curFileTg = gtfsPath + "/stop_times.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeStopTimes(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
if (!sourceFeed->getPublisherUrl().empty() &&
!sourceFeed->getPublisherName().empty()) {
curFile = gtfsPath + "/.feed_info.txt";
curFileTg = gtfsPath + "/feed_info.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFeedInfo(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
return true;
}
// ____________________________________________________________________________
bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const {
auto csvw = ad::cppgtfs::Writer::getFeedInfoCsvw(os);
csvw.flushLine();
csvw.writeString(f->getPublisherName());
csvw.writeString(f->getPublisherUrl());
csvw.writeString(f->getLang());
if (!f->getStartDate().empty())
csvw.writeInt(f->getStartDate().getYYYYMMDD());
else
csvw.skip();
if (!f->getEndDate().empty())
csvw.writeInt(f->getEndDate().getYYYYMMDD());
else
csvw.skip();
csvw.writeString(f->getVersion());
csvw.flushLine();
return true;
}
// ____________________________________________________________________________
bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/agency.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getAgencyCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Agency fa;
auto flds = Parser::getAgencyFlds(&csvp);
while (p.nextAgency(&csvp, &fa, flds)) {
w.writeAgency(fa, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/stops.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopsCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Stop s;
auto flds = Parser::getStopFlds(&csvp);
while (p.nextStop(&csvp, &s, flds)) {
w.writeStop(s, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/routes.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getRoutesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Route s;
auto flds = Parser::getRouteFlds(&csvp);
while (p.nextRoute(&csvp, &s, flds)) {
w.writeRoute(s, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/calendar.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Calendar c;
auto flds = Parser::getCalendarFlds(&csvp);
while (p.nextCalendar(&csvp, &c, flds)) {
w.writeCalendar(c, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed,
std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarDatesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::CalendarDate c;
auto flds = Parser::getCalendarDateFlds(&csvp);
while (p.nextCalendarDate(&csvp, &c, flds)) {
w.writeCalendarDate(c, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/frequencies.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFrequencyCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Frequency f;
auto flds = Parser::getFrequencyFlds(&csvp);
while (p.nextFrequency(&csvp, &f, flds)) {
w.writeFrequency(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/transfers.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getTransfersCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Transfer t;
auto flds = Parser::getTransfersFlds(&csvp);
while (p.nextTransfer(&csvp, &t, flds)) {
w.writeTransfer(t, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFaresCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Fare f;
auto flds = Parser::getFareFlds(&csvp);
while (p.nextFare(&csvp, &f, flds)) {
w.writeFare(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/fare_rules.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFareRulesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::FareRule f;
auto flds = Parser::getFareRuleFlds(&csvp);
while (p.nextFareRule(&csvp, &f, flds)) {
w.writeFareRule(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/shapes.txt").c_str());
CsvWriter csvw = ad::cppgtfs::Writer::getShapesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::ShapePoint sp;
ad::cppgtfs::Writer w;
if (fs.good()) {
CsvParser csvp(&fs);
Parser p;
auto flds = Parser::getShapeFlds(&csvp);
std::string curShapeId;
std::string curSkipShapeId;
while (p.nextShapePoint(&csvp, &sp, flds)) {
if (sp.id == curSkipShapeId) continue;
if (sp.id != curShapeId) {
if (sourceFeed->getShapes().has(sp.id)) {
curShapeId = sp.id;
} else {
curSkipShapeId = sp.id;
continue;
}
}
w.writeShapePoint(sp, &csvw);
}
}
sourceFeed->getShapes().open();
while (sourceFeed->getShapes().nextStoragePt(&sp)) {
w.writeShapePoint(sp, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeTrips(gtfs::Feed* sourceFeed, std::ostream* os) const {
ad::cppgtfs::Writer w;
bool hasFreqs = false;
CsvWriter csvw = ad::cppgtfs::Writer::getTripsCsvw(os);
csvw.flushLine();
for (auto t : sourceFeed->getTrips()) {
if (t.getFrequencies().size()) hasFreqs = true;
w.writeTrip(t.getFlat(), &csvw);
}
return hasFreqs;
}
// ____________________________________________________________________________
bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/stop_times.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopTimesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::StopTime st;
auto flds = Parser::getStopTimeFlds(&csvp);
std::string curTripId;
Trip* cur = 0;
while (p.nextStopTime(&csvp, &st, flds)) {
// we may have changed to distance field
if (curTripId != st.trip) {
cur = sourceFeed->getTrips().get(st.trip);
curTripId = st.trip;
}
for (const auto& stN : cur->getStopTimes()) {
if (stN.getSeq() == st.sequence)
st.shapeDistTravelled = stN.getShapeDistanceTravelled();
}
w.writeStopTime(st, &csvw);
}
fs.close();
return true;
}
// ___________________________________________________________________________
void Writer::cannotWrite(const std::string& file, const std::string& file2) {
std::stringstream ss;
ss << "(temporary file for " << file2 << ") Could not write to file";
throw ad::cppgtfs::WriterException(ss.str(), file);
}

41
src/pfaedle/gtfs/Writer.h Normal file
View file

@ -0,0 +1,41 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_WRITER_H_
#define PFAEDLE_GTFS_WRITER_H_
#include <string>
#include "Feed.h"
namespace pfaedle {
namespace gtfs {
class Writer {
public:
Writer() {}
bool write(Feed* sourceFeed, const std::string& path) const;
private:
bool writeFeedInfo(Feed* f, std::ostream* os) const;
bool writeAgency(Feed* f, std::ostream* os) const;
bool writeStops(Feed* f, std::ostream* os) const;
bool writeRoutes(Feed* f, std::ostream* os) const;
bool writeCalendar(Feed* f, std::ostream* os) const;
bool writeCalendarDates(Feed* f, std::ostream* os) const;
bool writeFrequencies(Feed* f, std::ostream* os) const;
bool writeTransfers(Feed* f, std::ostream* os) const;
bool writeFares(Feed* f, std::ostream* os) const;
bool writeFareRules(Feed* f, std::ostream* os) const;
bool writeShapes(Feed* f, std::ostream* os) const;
bool writeTrips(Feed* f, std::ostream* os) const;
bool writeStopTimes(Feed* f, std::ostream* os) const;
static void cannotWrite(const std::string& file, const std::string& file2);
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_WRITER_H_

View file

@ -9,11 +9,12 @@
#include <string>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "util/String.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL;
using ad::cppgtfs::gtfs::Trip;
using pfaedle::gtfs::Trip;
namespace pfaedle {
namespace netgraph {
@ -22,7 +23,7 @@ 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<PFAEDLE_PRECISION> {
class EdgePL {
public:
EdgePL() {}
EdgePL(const LINE& l, const std::set<const Trip*>& trips)
@ -36,10 +37,10 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
util::json::Dict getAttrs() const {
util::json::Dict obj;
obj["num_trips"] = static_cast<int>(_trips.size());
obj["route_short_names"] = util::json::Array(
_routeShortNames.begin(), _routeShortNames.end());
obj["trip_short_names"] = util::json::Array(_tripShortNames.begin(),
_tripShortNames.end());
obj["route_short_names"] =
util::json::Array(_routeShortNames.begin(), _routeShortNames.end());
obj["trip_short_names"] =
util::json::Array(_tripShortNames.begin(), _tripShortNames.end());
return obj;
}

View file

@ -12,7 +12,6 @@
using util::geograph::GeoNodePL;
namespace pfaedle {
namespace netgraph {
@ -20,15 +19,13 @@ 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<PFAEDLE_PRECISION> {
class NodePL {
public:
NodePL() {}
NodePL(const POINT& geom) { _geom = geom; } // NOLINT
const POINT* getGeom() const { return &_geom; }
util::json::Dict getAttrs() const {
return util::json::Dict();
}
util::json::Dict getAttrs() const { return util::json::Dict(); }
private:
POINT _geom;

View file

@ -37,6 +37,31 @@ BOX BBoxIdx::getFullWebMercBox() const {
_root.box.getUpperRight().getY(), _root.box.getUpperRight().getX()));
}
// _____________________________________________________________________________
BOX BBoxIdx::getFullBox() const { return _root.box; }
// _____________________________________________________________________________
std::vector<util::geo::Box<double>> BBoxIdx::getLeafs() const {
std::vector<util::geo::Box<double>> ret;
getLeafsRec(_root, &ret);
return ret;
}
// _____________________________________________________________________________
void BBoxIdx::getLeafsRec(const BBoxIdxNd& nd,
std::vector<util::geo::Box<double>>* ret) const {
if (!nd.childs.size()) {
ret->push_back(nd.box);
return;
}
for (const auto& child : nd.childs) {
getLeafsRec(child, ret);
}
return;
}
// _____________________________________________________________________________
bool BBoxIdx::treeHas(const Point<double>& p, const BBoxIdxNd& nd) const {
if (!nd.childs.size()) return util::geo::contains(p, nd.box);

View file

@ -38,9 +38,15 @@ class BBoxIdx {
// Return the full total bounding box of this index
BOX getFullWebMercBox() const;
// Return the full total bounding box of this index
BOX getFullBox() const;
// Return the size of this index
size_t size() const;
// return the leaf bounding boxes of this idx
std::vector<Box<double>> getLeafs() const;
private:
double _padding;
size_t _size;
@ -50,6 +56,9 @@ class BBoxIdx {
void addToTree(const Box<double>& box, BBoxIdxNd* nd, size_t lvl);
bool treeHas(const Point<double>& p, const BBoxIdxNd& nd) const;
void getLeafsRec(const BBoxIdxNd& nd,
std::vector<util::geo::Box<double>>* ret) const;
static const size_t MAX_LVL = 5;
static constexpr double MIN_COM_AREA = 0.0;
};

View file

@ -43,8 +43,20 @@ using pfaedle::osm::OsmRel;
using pfaedle::osm::OsmNode;
using pfaedle::osm::EdgeGrid;
using pfaedle::osm::NodeGrid;
using pfaedle::osm::EqSearch;
using pfaedle::osm::BlockSearch;
using ad::cppgtfs::gtfs::Stop;
// _____________________________________________________________________________
bool EqSearch::operator()(const Node* cand, const StatInfo* si) const {
if (orphanSnap && cand->pl().getSI() &&
(!cand->pl().getSI()->getGroup() ||
cand->pl().getSI()->getGroup()->getStops().size() == 0)) {
return true;
}
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
}
// _____________________________________________________________________________
OsmBuilder::OsmBuilder() {}
@ -140,7 +152,8 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
POINT geom = *s->pl().getGeom();
NodePL pl = s->pl();
pl.getSI()->setIsFromOsm(false);
const auto& r = snapStation(g, &pl, &eg, &sng, opts, res, false, d);
const auto& r =
snapStation(g, &pl, &eg, &sng, opts, res, false, false, d);
groupStats(r);
for (auto n : r) {
// if the snapped station is very near to the original OSM
@ -153,32 +166,70 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
}
}
std::vector<const Stop*> notSnapped;
for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) {
double d = opts.maxSnapDistances[i];
for (auto& s : *fs) {
auto pl = plFromGtfs(s.first, opts);
StatGroup* group =
groupStats(snapStation(g, &pl, &eg, &sng, opts, res,
i == opts.maxSnapDistances.size() - 1, d));
StatGroup* group = groupStats(
snapStation(g, &pl, &eg, &sng, opts, res,
i == opts.maxSnapDistances.size() - 1, false, d));
if (group) {
group->addStop(s.first);
(*fs)[s.first] = *group->getNodes().begin();
} else if (i == opts.maxSnapDistances.size() - 1) {
LOG(VDEBUG) << "Could not snap station "
<< "(" << pl.getSI()->getName() << ")"
<< " (" << s.first->getLat() << "," << s.first->getLng()
<< ") in normal run, trying again later in orphan mode.";
notSnapped.push_back(s.first);
}
}
}
if (notSnapped.size())
LOG(VDEBUG) << notSnapped.size() << " stations could not be snapped in "
"normal run, trying again in orphan "
"mode.";
// try again, but aggressively snap to orphan OSM stations which have
// not been assigned to any GTFS stop yet
for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) {
double d = opts.maxSnapDistances[i];
for (auto& s : notSnapped) {
auto pl = plFromGtfs(s, opts);
StatGroup* group = groupStats(
snapStation(g, &pl, &eg, &sng, opts, res,
i == opts.maxSnapDistances.size() - 1, true, d));
if (group) {
group->addStop(s);
// add the added station name as an alt name to ensure future
// similarity
for (auto n : group->getNodes()) {
if (n->pl().getSI())
n->pl().getSI()->addAltName(pl.getSI()->getName());
}
(*fs)[s] = *group->getNodes().begin();
} else if (i ==
opts.maxSnapDistances.size() - 1) { // only fail on last
// finally give up
// add a group with only this stop in it
StatGroup* dummyGroup = new StatGroup();
Node* dummyNode = g->addNd(pl);
dummyNode->pl().getSI()->setGroup(dummyGroup);
dummyGroup->addNode(dummyNode);
dummyGroup->addStop(s.first);
(*fs)[s.first] = dummyNode;
dummyGroup->addStop(s);
(*fs)[s] = dummyNode;
LOG(WARN) << "Could not snap station "
<< "(" << pl.getSI()->getName() << ")"
<< " (" << s.first->getLat() << "," << s.first->getLng()
<< ")";
<< " (" << s->getLat() << "," << s->getLng() << ")";
}
}
}
@ -188,7 +239,7 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
deleteOrphNds(g);
LOG(VDEBUG) << "Deleting orphan edges...";
deleteOrphEdgs(g);
deleteOrphEdgs(g, opts);
LOG(VDEBUG) << "Collapsing edges...";
collapseEdges(g);
@ -197,7 +248,7 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
deleteOrphNds(g);
LOG(VDEBUG) << "Deleting orphan edges...";
deleteOrphEdgs(g);
deleteOrphEdgs(g, opts);
LOG(VDEBUG) << "Writing graph components...";
// the restrictor is needed here to prevent connections in the graph
@ -223,6 +274,89 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
<< " edges and " << comps << " connected component(s)";
}
// _____________________________________________________________________________
void OsmBuilder::overpassQryWrite(std::ostream* out,
const std::vector<OsmReadOpts>& opts,
const BBoxIdx& latLngBox) const {
OsmIdSet bboxNodes, noHupNodes;
MultAttrMap emptyF;
RelLst rels;
OsmIdList ways;
RelMap nodeRels, wayRels;
// TODO(patrick): not needed here!
Restrictions rests;
NIdMap nodes;
// always empty
NIdMultMap multNodes;
util::xml::XmlWriter wr(out, true, 4);
*out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
wr.openComment();
wr.writeText(" - written by pfaedle -");
wr.closeTag();
wr.openTag("osm-script",
{{"timeout", "99999"}, {"element-limit", "1073741824"}});
OsmFilter filter;
for (const OsmReadOpts& o : opts) {
filter = filter.merge(OsmFilter(o.keepFilter, o.dropFilter));
}
wr.openTag("union");
size_t c = 0;
for (auto box : latLngBox.getLeafs()) {
if (box.getLowerLeft().getX() > box.getUpperRight().getX()) continue;
c++;
wr.openComment();
wr.writeText(std::string("Bounding box #") + std::to_string(c) + " (" +
std::to_string(box.getLowerLeft().getY()) + ", " +
std::to_string(box.getLowerLeft().getX()) + ", " +
std::to_string(box.getUpperRight().getY()) + ", " +
std::to_string(box.getUpperRight().getX()) + ")");
wr.closeTag();
for (auto t : std::vector<std::string>{"way", "node", "relation"}) {
for (auto r : filter.getKeepRules()) {
for (auto val : r.second) {
if (t == "way" && (val.second & OsmFilter::WAY)) continue;
if (t == "relation" && (val.second & OsmFilter::REL)) continue;
if (t == "node" && (val.second & OsmFilter::NODE)) continue;
wr.openTag("query", {{"type", t}});
if (val.first == "*")
wr.openTag("has-kv", {{"k", r.first}});
else
wr.openTag("has-kv", {{"k", r.first}, {"v", val.first}});
wr.closeTag();
wr.openTag("bbox-query",
{{"s", std::to_string(box.getLowerLeft().getY())},
{"w", std::to_string(box.getLowerLeft().getX())},
{"n", std::to_string(box.getUpperRight().getY())},
{"e", std::to_string(box.getUpperRight().getX())}});
wr.closeTag();
wr.closeTag();
}
}
}
}
wr.closeTag();
wr.openTag("union");
wr.openTag("item");
wr.closeTag();
wr.openTag("recurse", {{"type", "down"}});
wr.closeTag();
wr.closeTag();
wr.openTag("print");
wr.closeTags();
}
// _____________________________________________________________________________
void OsmBuilder::filterWrite(const std::string& in, const std::string& out,
const std::vector<OsmReadOpts>& opts,
@ -250,8 +384,15 @@ void OsmBuilder::filterWrite(const std::string& in, const std::string& out,
outstr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
wr.openTag("osm");
// TODO(patrick): write bounding box tag
wr.openTag(
"bounds",
{{"minlat", std::to_string(latLngBox.getFullBox().getLowerLeft().getY())},
{"minlon", std::to_string(latLngBox.getFullBox().getLowerLeft().getX())},
{"maxlat",
std::to_string(latLngBox.getFullBox().getUpperRight().getY())},
{"maxlon",
std::to_string(latLngBox.getFullBox().getUpperRight().getX())}});
wr.closeTag();
OsmFilter filter;
AttrKeySet attrKeys[3] = {};
@ -1071,8 +1212,7 @@ EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size,
}
// _____________________________________________________________________________
NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size,
const BOX& webMercBox,
NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox,
bool which) const {
NodeGrid ret(size, size, webMercBox, false);
for (auto* n : *g->getNds()) {
@ -1158,9 +1298,10 @@ bool OsmBuilder::isBlocked(const Edge* e, const StatInfo* si, const POINT& p,
// _____________________________________________________________________________
Node* OsmBuilder::eqStatReach(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns,
double minAngle) const {
return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, EqSearch());
double maxD, int maxFullTurns, double minAngle,
bool orphanSnap) const {
return depthSearch(e, si, p, maxD, maxFullTurns, minAngle,
EqSearch(orphanSnap));
}
// _____________________________________________________________________________
@ -1187,8 +1328,7 @@ std::set<Node*> OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng,
std::set<Node*> ret;
double distor = webMercDistFactor(*s.getGeom());
std::set<Node*> neighs;
BOX box =
util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
ng->get(box, &neighs);
for (auto* n : neighs) {
@ -1205,8 +1345,7 @@ std::set<Node*> OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng,
Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const {
double distor = webMercDistFactor(*s.getGeom());
std::set<Node*> neighs;
BOX box =
util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
ng->get(box, &neighs);
Node* ret = 0;
@ -1229,7 +1368,7 @@ Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const {
std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
NodeGrid* sng, const OsmReadOpts& opts,
Restrictor* restor, bool surrHeur,
double d) const {
bool orphSnap, double d) const {
assert(s->getSI());
std::set<Node*> ret;
@ -1239,10 +1378,14 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
if (pq.empty() && surrHeur) {
// no station found in the first round, try again with the nearest
// surrounding
// station with matching name
// surrounding station with matching name
const Node* best = getMatchingNd(*s, sng, opts.maxSnapFallbackHeurDistance);
if (best) getEdgCands(*best->pl().getGeom(), &pq, eg, d);
if (best) {
getEdgCands(*best->pl().getGeom(), &pq, eg, d);
} else {
// if still no luck, get edge cands in fallback snap distance
getEdgCands(*s->getGeom(), &pq, eg, opts.maxSnapFallbackHeurDistance);
}
}
while (!pq.empty()) {
@ -1254,7 +1397,7 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
Node* eq = 0;
if (!(eq = eqStatReach(e, s->getSI(), geom, 2 * d, 0,
opts.maxAngleSnapReach))) {
opts.maxAngleSnapReach, orphSnap))) {
if (e->pl().lvl() > opts.maxSnapLevel) continue;
if (isBlocked(e, s->getSI(), geom, opts.maxBlockDistance, 0,
opts.maxAngleSnapReach)) {
@ -1309,11 +1452,6 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
}
}
// get surrounding nodes
// TODO(patrick): own distance configuration for this!
const auto& sur = getMatchingNds(*s, sng, opts.maxGroupSearchDistance);
ret.insert(sur.begin(), sur.end());
return ret;
}
@ -1336,7 +1474,10 @@ StatGroup* OsmBuilder::groupStats(const NodeSet& s) const {
}
}
if (!used) delete ret;
if (!used) {
delete ret;
return 0;
}
return ret;
}
@ -1507,7 +1648,7 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts,
}
// _____________________________________________________________________________
void OsmBuilder::deleteOrphEdgs(Graph* g) const {
void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const {
size_t ROUNDS = 3;
for (size_t c = 0; c < ROUNDS; c++) {
for (auto i = g->getNds()->begin(); i != g->getNds()->end();) {
@ -1515,6 +1656,15 @@ void OsmBuilder::deleteOrphEdgs(Graph* g) const {
++i;
continue;
}
// check if the removal of this edge would transform a steep angle
// full turn at an intersection into a node 2 eligible for contraction
// if so, dont delete
if (keepFullTurn(*i, opts.fullTurnAngle)) {
++i;
continue;
}
i = g->delNd(*i);
continue;
i++;
@ -1706,3 +1856,43 @@ void OsmBuilder::writeSelfEdgs(Graph* g) const {
}
}
}
// _____________________________________________________________________________
bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) const {
if (n->getInDeg() + n->getOutDeg() != 1) return false;
const trgraph::Edge* e = 0;
if (n->getOutDeg())
e = n->getAdjListOut().front();
else
e = n->getAdjListIn().front();
auto other = e->getOtherNd(n);
if (other->getInDeg() + other->getOutDeg() == 3) {
const trgraph::Edge* a = 0;
const trgraph::Edge* b = 0;
for (auto f : other->getAdjListIn()) {
if (f != e && !a)
a = f;
else if (f != e && !b)
b = f;
}
for (auto f : other->getAdjListOut()) {
if (f != e && !a)
a = f;
else if (f != e && !b)
b = f;
}
auto ap = a->pl().backHop();
auto bp = b->pl().backHop();
if (a->getTo() != other) ap = a->pl().frontHop();
if (b->getTo() != other) bp = b->pl().frontHop();
return router::angSmaller(ap, *other->pl().getGeom(), bp, ang);
}
return false;
}

View file

@ -59,10 +59,10 @@ struct SearchFunc {
};
struct EqSearch : public SearchFunc {
explicit EqSearch(bool orphanSnap) : orphanSnap(orphanSnap) {}
double minSimi = 0.9;
bool operator()(const Node* cand, const StatInfo* si) const {
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
}
bool orphanSnap;
bool operator()(const Node* cand, const StatInfo* si) const;
};
struct BlockSearch : public SearchFunc {
@ -91,6 +91,11 @@ class OsmBuilder {
const BBoxIdx& box, size_t gridSize, router::FeedStops* fs,
Restrictor* res);
// Based on the list of options, output an overpass XML query for getting
// the data needed for routing
void overpassQryWrite(std::ostream* out, const std::vector<OsmReadOpts>& opts,
const BBoxIdx& latLngBox) const;
// 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
@ -170,7 +175,7 @@ class OsmBuilder {
void writeGeoms(Graph* g) const;
void deleteOrphNds(Graph* g) const;
void deleteOrphEdgs(Graph* g) const;
void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const;
double dist(const Node* a, const Node* b) const;
double webMercDist(const Node* a, const Node* b) const;
double webMercDistFactor(const POINT& a) const;
@ -198,13 +203,14 @@ class OsmBuilder {
NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
const OsmReadOpts& opts, Restrictor* restor, bool surHeur,
double maxD) const;
bool orphSnap, 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 POINT& p,
double maxD, int maxFullTurns, double maxAng) const;
double maxD, int maxFullTurns, double maxAng,
bool orph) const;
Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double minAngle,
@ -243,6 +249,8 @@ class OsmBuilder {
bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const;
bool keepFullTurn(const trgraph::Node* n, double ang) const;
std::map<TransitEdgeLine, TransitEdgeLine*> _lines;
std::map<size_t, TransitEdgeLine*> _relLines;
};

View file

@ -102,12 +102,12 @@ uint64_t OsmFilter::contained(const AttrMap& attrs, const Attr& attr) {
// _____________________________________________________________________________
uint8_t OsmFilter::level(const AttrMap& attrs) const {
// the best matching level is always returned
for (int16_t i = 0; i < 7; i++) {
for (int16_t i = 0; i < 8; 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;
if (valMatches(kv.second, val.first)) return i;
}
}
}
@ -169,7 +169,7 @@ std::vector<std::string> OsmFilter::getAttrKeys() const {
for (const auto& kv : _noRestr) {
ret.push_back(kv.first);
}
for (uint8_t i = 0; i < 7; i++) {
for (uint8_t i = 0; i < 8; i++) {
for (const auto& kv : *(_levels + i)) {
ret.push_back(kv.first);
}
@ -191,27 +191,6 @@ OsmFilter OsmFilter::merge(const OsmFilter& other) const {
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);
}
@ -258,3 +237,13 @@ uint64_t OsmFilter::posRestr(const AttrMap& attrs) const {
if (contained(attrs, _noRestr, ALL)) return false;
return (contained(attrs, _posRestr, ALL));
}
// _____________________________________________________________________________
const pfaedle::osm::MultAttrMap& OsmFilter::getKeepRules() const {
return _keep;
}
// _____________________________________________________________________________
const pfaedle::osm::MultAttrMap& OsmFilter::getDropRules() const {
return _drop;
}

View file

@ -33,6 +33,9 @@ class OsmFilter {
OsmFilter merge(const OsmFilter& other) const;
const MultAttrMap& getKeepRules() const;
const MultAttrMap& getDropRules() const;
std::string toString() const;
static bool valMatches(const std::string& a, const std::string& b, bool m);

View file

@ -113,7 +113,7 @@ struct OsmReadOpts {
MultAttrMap noHupFilter;
MultAttrMap keepFilter;
MultAttrMap levelFilters[7];
MultAttrMap levelFilters[8];
MultAttrMap dropFilter;
MultAttrMap oneWayFilter;
MultAttrMap oneWayFilterRev;
@ -136,7 +136,6 @@ struct OsmReadOpts {
double maxAngleSnapReach;
std::vector<double> maxSnapDistances;
double maxSnapFallbackHeurDistance;
double maxGroupSearchDistance;
double maxBlockDistance;
double maxOsmStationDistance;
@ -144,6 +143,8 @@ struct OsmReadOpts {
// TODO(patrick): this is not implemented yet
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
double fullTurnAngle;
// restriction system
MultAttrMap restrPosRestr;
MultAttrMap restrNegRestr;
@ -179,7 +180,6 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
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 &&
@ -188,6 +188,7 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
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 &&
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 &&
a.restrPosRestr == b.restrPosRestr &&
a.restrNegRestr == b.restrNegRestr &&
a.noRestrFilter == b.noRestrFilter;

View file

@ -60,11 +60,28 @@ inline double lineSimi(const std::string& a, const std::string& b) {
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;
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;
}
}
return 0;

View file

@ -82,21 +82,6 @@ util::json::Dict EdgePL::getAttrs() 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";
return obj;

View file

@ -17,7 +17,7 @@ using util::geograph::GeoEdgePL;
namespace pfaedle {
namespace router {
class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
class EdgePL {
public:
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
const LINE* getGeom() const;

View file

@ -7,8 +7,8 @@
#include <set>
#include <string>
#include <vector>
#include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "pfaedle/trgraph/Graph.h"
@ -67,90 +67,35 @@ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) {
}
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() : _cost(0) {}
explicit EdgeCost(double cost) : _cost(cost) {}
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 reachPen, const RoutingOpts* o) {
if (!o) {
_cost = mDist + reachPen;
} else {
_cost = mDist * o->levelPunish[0] + mDistLvl1 * o->levelPunish[1] +
mDistLvl2 * o->levelPunish[2] + mDistLvl3 * o->levelPunish[3] +
mDistLvl4 * o->levelPunish[4] + mDistLvl5 * o->levelPunish[5] +
mDistLvl6 * o->levelPunish[6] + mDistLvl7 * o->levelPunish[7] +
oneWayMeters * o->oneWayPunishFac +
oneWayEdges * o->oneWayEdgePunish +
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
fullTurns * o->fullTurnPunishFac +
passThru * o->passThruStationsPunish + reachPen;
}
}
double getTotalMeters() const {
return meterDist + meterDistLvl1 + meterDistLvl2 + meterDistLvl3 +
meterDistLvl4 + meterDistLvl5 + meterDistLvl6 + meterDistLvl7;
}
float _cost;
double getValue() const { return _cost; }
};
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);
return EdgeCost(a.getValue() + b.getValue());
}
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
@ -165,9 +110,9 @@ inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() > b.getValue();
}
template<typename F>
template <typename F>
inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& t,
double ang) {
double ang) {
if (util::geo::innerProd(m, f, t) < ang) return 1;
return 0;
}

View file

@ -18,7 +18,7 @@ using util::geograph::GeoNodePL;
namespace pfaedle {
namespace router {
class NodePL : public GeoNodePL<PFAEDLE_PRECISION> {
class NodePL {
public:
NodePL() : _n(0) {}
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT

View file

@ -191,7 +191,8 @@ double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
}
// _____________________________________________________________________________
Router::Router(size_t numThreads) : _cache(numThreads) {
Router::Router(size_t numThreads, bool caching)
: _cache(numThreads), _caching(caching) {
for (size_t i = 0; i < numThreads; i++) {
_cache[i] = new Cache();
}
@ -219,6 +220,9 @@ bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const {
HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
assert(a.size());
assert(b.size());
double pend = 0;
for (size_t i = 0; i < a.size(); i++) {
for (size_t j = 0; j < b.size(); j++) {
@ -231,6 +235,7 @@ HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
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();
@ -556,6 +561,7 @@ void Router::nestedCache(const EdgeList* el,
const std::set<trgraph::Edge*>& froms,
const CostFunc& cost,
const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (el->size() == 0) return;
// iterate over result edges backwards
EdgeList curEdges;
@ -586,7 +592,7 @@ std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
const RoutingAttrs& rAttrs) const {
std::set<trgraph::Edge*> ret;
for (auto to : tos) {
if ((*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) {
if (_caching && (*_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;
@ -601,6 +607,7 @@ std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
// _____________________________________________________________________________
void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (from == to) return;
(*_cache[omp_get_thread_num()])[rAttrs][from][to] =
std::pair<EdgeCost, EdgeList>(c, *edges);

View file

@ -137,7 +137,7 @@ struct CombCostFunc
class Router {
public:
// Init this router with caches for numThreads threads
explicit Router(size_t numThreads);
explicit Router(size_t numThreads, bool caching);
~Router();
// Find the most likely path through the graph for a node candidate route.
@ -163,6 +163,7 @@ class Router {
private:
mutable std::vector<Cache*> _cache;
bool _caching;
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;

View file

@ -9,6 +9,7 @@
#define omp_get_num_procs() 1
#endif
#include <exception>
#include <map>
#include <mutex>
#include <unordered_map>
@ -16,6 +17,8 @@
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/StopTime.h"
#include "pfaedle/osm/OsmBuilder.h"
#include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/trgraph/StatGroup.h"
@ -43,38 +46,36 @@ 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 pfaedle::gtfs::Trip;
using pfaedle::gtfs::Feed;
using pfaedle::gtfs::StopTime;
using ad::cppgtfs::gtfs::ShapePoint;
// _____________________________________________________________________________
ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots,
const config::MotConfig& motCfg,
ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed,
MOTs mots, const config::MotConfig& motCfg,
eval::Collector* ecoll, const config::Config& cfg)
: _feed(feed),
_evalFeed(evalFeed),
_mots(mots),
_motCfg(motCfg),
_ecoll(ecoll),
_cfg(cfg),
_crouter(omp_get_num_procs()),
_crouter(omp_get_num_procs(), cfg.useCaching),
_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()) {
if (!_cfg.shapeTripId.empty() && t.getId() != _cfg.shapeTripId) continue;
if (_mots.count(t.getRoute()->getType()) &&
_motCfg.mots.count(t.getRoute()->getType())) {
for (auto st : t.getStopTimes()) {
_stops[st.getStop()] = 0;
}
}
@ -95,28 +96,34 @@ const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const {
// _____________________________________________________________________________
LINE ShapeBuilder::shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) {
const router::EdgeListHops& res = route(ncr, rAttrs);
try {
const router::EdgeListHops& res = route(ncr, rAttrs);
LINE 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());
LINE 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);
}
last = e->getOtherNd(last);
}
}
return l;
return l;
} catch (const std::runtime_error& e) {
LOG(ERROR) << e.what();
return LINE();
}
}
// _____________________________________________________________________________
@ -193,9 +200,9 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
Clusters clusters = clusterTrips(_feed, _mots);
LOG(DEBUG) << "Clustered trips into " << clusters.size() << " clusters.";
std::map<ad::cppgtfs::gtfs::Shape*, size_t> shpUsage;
std::map<std::string, size_t> shpUsage;
for (auto t : _feed->getTrips()) {
if (t.second->getShape()) shpUsage[t.second->getShape()]++;
if (!t.getShape().empty()) shpUsage[t.getShape()]++;
}
// to avoid unfair load balance on threads
@ -223,7 +230,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
<< "%, " << (EDijkstra::ITERS - oiters) << " iters, "
/**
TODO: this is actually misleading. We are counting the
Dijkstra iterations, but the measuring them against
Dijkstra iterations, but measuring them against
the total running time (including all overhead + HMM solve)
<< tput "
<< (static_cast<double>(EDijkstra::ITERS - oiters)) /
@ -249,7 +256,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
}
std::vector<double> distances;
ad::cppgtfs::gtfs::Shape* shp =
const ad::cppgtfs::gtfs::Shape& shp =
getGtfsShape(cshp, clusters[i][0], &distances);
LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations.";
@ -259,14 +266,14 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
for (auto t : clusters[i]) {
if (_cfg.evaluate) {
_ecoll->add(t, t->getShape(), shp, distances);
_ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp,
distances);
}
if (t->getShape() && shpUsage[t->getShape()] > 0) {
if (!t->getShape().empty() && shpUsage[t->getShape()] > 0) {
shpUsage[t->getShape()]--;
if (shpUsage[t->getShape()] == 0) {
_feed->getShapes().remove(t->getShape()->getId());
delete t->getShape();
_feed->getShapes().remove(t->getShape());
}
}
setShape(t, shp, distances);
@ -295,27 +302,25 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
}
// _____________________________________________________________________________
void ShapeBuilder::setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s,
void ShapeBuilder::setShape(Trip* t, const 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]);
for (const auto& st : t->getStopTimes()) {
const_cast<StopTime<Stop>&>(st).setShapeDistanceTravelled(distances[i]);
i++;
}
t->setShape(s);
std::lock_guard<std::mutex> guard(_shpMutex);
_feed->getShapes().add(s);
// TODO(patrick):
t->setShape(_feed->getShapes().add(s));
}
// _____________________________________________________________________________
ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
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));
ad::cppgtfs::gtfs::Shape ret(getFreeShapeId(t));
assert(shp.hops.size() == t->getStopTimes().size() - 1);
@ -338,7 +343,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
last = *hop.start->pl().getGeom();
if (dist - lastDist > 0.01) {
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++;
lastDist = dist;
}
@ -349,11 +354,12 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) {
ll = webMercToLatLng<PFAEDLE_PRECISION>(
hop.end->pl().getGeom()->getX(), hop.end->pl().getGeom()->getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
ret.addPoint(ShapePoint(ll.getY(), ll.getX(), 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()) {
@ -367,7 +373,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) {
POINT ll =
webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++;
lastDist = dist;
}
@ -383,7 +389,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) {
POINT ll =
webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++;
lastDist = dist;
}
@ -447,33 +453,30 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const {
}
// _____________________________________________________________________________
BBoxIdx ShapeBuilder::getPaddedGtfsBox(const Feed* feed, double pad,
const MOTs& mots, const std::string& tid,
bool dropShapes) {
osm::BBoxIdx box(pad);
void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots,
const std::string& tid, bool dropShapes,
osm::BBoxIdx* box) {
for (const auto& t : feed->getTrips()) {
if (!tid.empty() && t.second->getId() != tid) continue;
if (tid.empty() && t.second->getShape() && !dropShapes) continue;
if (t.second->getStopTimes().size() < 2) continue;
if (mots.count(t.second->getRoute()->getType())) {
if (!tid.empty() && t.getId() != tid) continue;
if (tid.empty() && !t.getShape().empty() && !dropShapes) continue;
if (t.getStopTimes().size() < 2) continue;
if (mots.count(t.getRoute()->getType())) {
DBox cur;
for (const auto& st : t.second->getStopTimes()) {
for (const auto& st : t.getStopTimes()) {
cur = extendBox(DPoint(st.getStop()->getLng(), st.getStop()->getLat()),
cur);
}
box.add(cur);
box->add(cur);
}
}
return box;
}
// _____________________________________________________________________________
void ShapeBuilder::buildGraph() {
osm::OsmBuilder osmBuilder;
osm::BBoxIdx box =
getPaddedGtfsBox(_feed, 2500, _mots, _cfg.shapeTripId, _cfg.dropShapes);
osm::BBoxIdx box(BOX_PADDING);
getGtfsBox(_feed, _mots, _cfg.shapeTripId, _cfg.dropShapes, &box);
osmBuilder.read(_cfg.osmPath, _motCfg.osmBuildOpts, &_g, box, _cfg.gridSize,
getFeedStops(), &_restr);
@ -497,6 +500,11 @@ NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const {
for (const auto& st : trip->getStopTimes()) {
ncr[i] = getNodeCands(st.getStop());
if (ncr[i].size() == 0) {
throw std::runtime_error("No node candidate found for station '" +
st.getStop()->getName() + "' on trip '" +
trip->getId() + "'");
}
i++;
}
return ncr;
@ -535,29 +543,29 @@ Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) {
size_t j = 0;
Clusters ret;
for (const auto& trip : f->getTrips()) {
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()))
for (auto& trip : f->getTrips()) {
if (!trip.getShape().empty() && !_cfg.dropShapes) continue;
if (trip.getStopTimes().size() < 2) continue;
if (!mots.count(trip.getRoute()->getType()) ||
!_motCfg.mots.count(trip.getRoute()->getType()))
continue;
bool found = false;
auto spair = StopPair(trip.second->getStopTimes().begin()->getStop(),
trip.second->getStopTimes().rbegin()->getStop());
auto spair = StopPair(trip.getStopTimes().begin()->getStop(),
trip.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);
if (routingEqual(ret[c[i]][0], &trip)) {
ret[c[i]].push_back(&trip);
found = true;
break;
}
}
if (!found) {
ret.push_back({trip.second});
ret.push_back(Cluster{&trip});
// explicit call to write render attrs to cache
getRAttrs(trip.second);
getRAttrs(&trip);
clusterIdx[spair].push_back(ret.size() - 1);
}
}

View file

@ -16,6 +16,7 @@
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/PfaedleConfig.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Misc.h"
@ -27,8 +28,8 @@ namespace pfaedle {
namespace router {
using ad::cppgtfs::gtfs::Stop;
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Feed;
using pfaedle::gtfs::Trip;
using pfaedle::gtfs::Feed;
struct Shape {
router::EdgeListHops hops;
@ -48,8 +49,9 @@ typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
*/
class ShapeBuilder {
public:
ShapeBuilder(Feed* feed, MOTs mots, const config::MotConfig& motCfg,
eval::Collector* ecoll, const config::Config& cfg);
ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots,
const config::MotConfig& motCfg, eval::Collector* ecoll,
const config::Config& cfg);
void shape(pfaedle::netgraph::Graph* ng);
@ -66,12 +68,13 @@ class ShapeBuilder {
const trgraph::Graph* getGraph() const;
static osm::BBoxIdx getPaddedGtfsBox(const Feed* feed, double pad,
const MOTs& mots, const std::string& tid,
bool dropShapes);
static void getGtfsBox(const Feed* feed, const MOTs& mots,
const std::string& tid, bool dropShapes,
osm::BBoxIdx* box);
private:
Feed* _feed;
ad::cppgtfs::gtfs::Feed* _evalFeed;
MOTs _mots;
config::MotConfig _motCfg;
eval::Collector* _ecoll;
@ -101,10 +104,10 @@ class ShapeBuilder {
std::string getFreeShapeId(Trip* t);
ad::cppgtfs::gtfs::Shape* getGtfsShape(const Shape& shp, 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,
void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<double>& dists);
router::NodeCandRoute getNCR(Trip* trip) const;

View file

@ -39,7 +39,7 @@ EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
}
_flines[_l]++;
for (auto l : _lines) addLine(l);
for (auto l : pl._lines) addLine(l);
}
// _____________________________________________________________________________
@ -82,7 +82,9 @@ double EdgePL::getLength() const { return _length; }
// _____________________________________________________________________________
void EdgePL::addLine(const TransitEdgeLine* l) {
if (_lines.insert(l).second) {
if (std::find(_lines.begin(), _lines.end(), l) == _lines.end()) {
_lines.reserve(_lines.size() + 1);
_lines.push_back(l);
if (_tlines.count(l))
_tlines[l]++;
else
@ -96,7 +98,7 @@ void EdgePL::addLines(const std::vector<TransitEdgeLine*>& l) {
}
// _____________________________________________________________________________
const std::set<const TransitEdgeLine*>& EdgePL::getLines() const {
const std::vector<const TransitEdgeLine*>& EdgePL::getLines() const {
return _lines;
}

View file

@ -45,7 +45,7 @@ inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
/*
* An edge payload class for the transit graph.
*/
class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
class EdgePL {
public:
EdgePL();
~EdgePL();
@ -101,7 +101,7 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
void addLines(const std::vector<TransitEdgeLine*>& l);
// Return the TransitEdgeLines stored for this payload
const std::set<const TransitEdgeLine*>& getLines() const;
const std::vector<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
@ -123,7 +123,7 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
LINE* _l;
std::set<const TransitEdgeLine*> _lines;
std::vector<const TransitEdgeLine*> _lines;
static void unRefTLine(const TransitEdgeLine* l);

View file

@ -26,7 +26,7 @@ struct Component {
/*
* A node payload class for the transit graph.
*/
class NodePL : public GeoNodePL<PFAEDLE_PRECISION> {
class NodePL {
public:
NodePL();
NodePL(const NodePL& pl); // NOLINT