From 7835242cb811f01b032afe9b906ce381846f1256 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 14 Jan 2019 12:44:59 +0100 Subject: [PATCH 001/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index 251c8ae..7bdfeff 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 251c8aedf329a557fa95811e8bc2388fc6918cb7 +Subproject commit 7bdfeffcbe5006ef74898e6dd760027a0dfa806e From 8fb21919f3a526bb73b8fd9059c4ff476e01ce36 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 14 Jan 2019 19:37:50 +0100 Subject: [PATCH 002/182] only fail if not sort-in succeeded at all --- src/pfaedle/osm/OsmBuilder.cpp | 65 ++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index efa5f4c..bac40ee 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -168,10 +168,11 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, std::vector 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); + for (auto& s : *fs) { + bool snapped = false; + auto pl = plFromGtfs(s.first, opts); + for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { + double d = opts.maxSnapDistances[i]; StatGroup* group = groupStats( snapStation(g, &pl, &eg, &sng, opts, res, @@ -180,14 +181,16 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, 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); + snapped = true; } } + if (!snapped) { + 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()) @@ -197,10 +200,11 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, // 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); + for (auto& s : notSnapped) { + bool snapped = false; + auto pl = plFromGtfs(s, opts); + for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { + double d = opts.maxSnapDistances[i]; StatGroup* group = groupStats( snapStation(g, &pl, &eg, &sng, opts, res, @@ -215,23 +219,24 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, 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); - (*fs)[s] = dummyNode; - LOG(WARN) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; + snapped = true; } } + if (!snapped) { + // 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); + (*fs)[s] = dummyNode; + LOG(WARN) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s->getLat() << "," << s->getLng() << ")"; + } } } @@ -299,7 +304,7 @@ void OsmBuilder::overpassQryWrite(std::ostream* out, wr.writeText(" - written by pfaedle -"); wr.closeTag(); wr.openTag("osm-script", - {{"timeout", "99999"}, {"element-limit", "1073741824"}}); + {{"timeout", "99999"}, {"element-limit", "2073741824"}}); OsmFilter filter; From 2fb157ef373edc957e55405fa6bc8d18f087ef8f Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 15 Jan 2019 19:10:10 +0100 Subject: [PATCH 003/182] dont output warning for unsnap if station lies outside the GTFS bounding box (because the trip it belongs to was filtered for some reason) --- src/pfaedle/osm/OsmBuilder.cpp | 24 +++++++++++++++++++----- src/pfaedle/router/ShapeBuilder.cpp | 5 ++--- src/pfaedle/router/ShapeBuilder.h | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index bac40ee..e340c60 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -189,6 +189,11 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, << "(" << pl.getSI()->getName() << ")" << " (" << s.first->getLat() << "," << s.first->getLng() << ") in normal run, trying again later in orphan mode."; + if (!bbox.contains(*pl.getGeom())) { + LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() + << "' does not lie within the bounds for this graph and " + "may be a stray station"; + } notSnapped.push_back(s.first); } } @@ -233,9 +238,19 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, dummyGroup->addNode(dummyNode); dummyGroup->addStop(s); (*fs)[s] = dummyNode; - LOG(WARN) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; + if (!bbox.contains(*pl.getGeom())) { + LOG(VDEBUG) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s->getLat() << "," << s->getLng() << ")"; + LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() + << "' does not lie within the bounds for this graph and " + "may be a stray station"; + } else { + // only warn if it is contained in the BBOX for this graph + LOG(WARN) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s->getLat() << "," << s->getLng() << ")"; + } } } } @@ -303,8 +318,7 @@ void OsmBuilder::overpassQryWrite(std::ostream* out, wr.openComment(); wr.writeText(" - written by pfaedle -"); wr.closeTag(); - wr.openTag("osm-script", - {{"timeout", "99999"}, {"element-limit", "2073741824"}}); + wr.openTag("osm-script"); OsmFilter filter; diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index 2beb663..988b7ad 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -460,6 +460,7 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, 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.getStopTimes()) { @@ -540,8 +541,6 @@ Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { std::map> clusterIdx; - size_t j = 0; - Clusters ret; for (auto& trip : f->getTrips()) { if (!trip.getShape().empty() && !_cfg.dropShapes) continue; @@ -549,13 +548,13 @@ Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { if (!mots.count(trip.getRoute()->getType()) || !_motCfg.mots.count(trip.getRoute()->getType())) continue; + bool found = false; 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)) { ret[c[i]].push_back(&trip); found = true; diff --git a/src/pfaedle/router/ShapeBuilder.h b/src/pfaedle/router/ShapeBuilder.h index 15c006e..3a88b93 100644 --- a/src/pfaedle/router/ShapeBuilder.h +++ b/src/pfaedle/router/ShapeBuilder.h @@ -105,7 +105,7 @@ class ShapeBuilder { std::string getFreeShapeId(Trip* t); ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t, - std::vector* hopDists); + std::vector* hopDists); void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, const std::vector& dists); From 4733b0c676fb247b6c7973acaac96276b2fd2769 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sun, 3 Feb 2019 12:48:48 +0100 Subject: [PATCH 004/182] refactoring --- src/configparser | 2 +- src/cppgtfs | 2 +- src/pfaedle/PfaedleMain.cpp | 62 ++-- src/pfaedle/config/MotConfig.h | 15 +- src/pfaedle/config/MotConfigReader.cpp | 83 ++++- src/pfaedle/gtfs/Writer.h | 1 + src/pfaedle/osm/OsmBuilder.cpp | 387 +++++++++++---------- src/pfaedle/osm/OsmBuilder.h | 81 ++--- src/pfaedle/osm/OsmReadOpts.h | 2 + src/pfaedle/router/Misc.h | 51 ++- src/pfaedle/router/Router.cpp | 128 ++++--- src/pfaedle/router/Router.h | 31 +- src/pfaedle/router/RoutingAttrs.h | 13 +- src/pfaedle/router/ShapeBuilder.cpp | 78 +---- src/pfaedle/router/ShapeBuilder.h | 12 +- src/pfaedle/trgraph/NodePL.cpp | 6 + src/pfaedle/trgraph/StatInfo.cpp | 3 + src/pfaedle/trgraph/StatInfo.h | 11 + src/util/Misc.h | 17 +- src/util/String.h | 31 ++ src/util/geo/Geo.h | 58 +++ src/util/geo/output/GeoGraphJsonOutput.h | 5 +- src/util/geo/output/GeoGraphJsonOutput.tpp | 14 +- src/util/geo/output/GeoJsonOutput.cpp | 13 +- src/util/geo/output/GeoJsonOutput.h | 1 + src/util/tests/TestMain.cpp | 73 ++++ 26 files changed, 774 insertions(+), 406 deletions(-) diff --git a/src/configparser b/src/configparser index 63fcb1d..afacc8b 160000 --- a/src/configparser +++ b/src/configparser @@ -1 +1 @@ -Subproject commit 63fcb1d54eb4889b376b76cafe140317326b5c56 +Subproject commit afacc8bc778bd1e38ae1c4466d1eb10ce92ba96e diff --git a/src/cppgtfs b/src/cppgtfs index 7bdfeff..212fdc7 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 7bdfeffcbe5006ef74898e6dd760027a0dfa806e +Subproject commit 212fdc7c8aff50b0f7a872ccb40850f8080eb402 diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 36ffb61..0f93134 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -25,6 +25,7 @@ #include "pfaedle/osm/OsmIdSet.h" #include "pfaedle/router/ShapeBuilder.h" #include "pfaedle/trgraph/Graph.h" +#include "pfaedle/trgraph/StatGroup.h" #include "util/geo/output/GeoGraphJsonOutput.h" #include "util/geo/output/GeoJsonOutput.h" #include "util/json/Writer.h" @@ -54,9 +55,7 @@ enum class RetCode { 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 getCfgPaths(const Config& cfg); // _____________________________________________________________________________ @@ -74,7 +73,7 @@ int main(int argc, char** argv) { cr.read(&cfg, argc, argv); std::vector gtfs(cfg.feedPaths.size()); - // feed containing the shapeas in memory for evaluation + // feed containing the shapes in memory for evaluation ad::cppgtfs::gtfs::Feed evalFeed; std::vector cfgPaths = getCfgPaths(cfg); @@ -202,19 +201,45 @@ int main(int argc, char** argv) { for (const auto& motCfg : motCfgReader.getConfigs()) { std::string filePost; - auto usedMots = getContMots(motCfg, cmdCfgMots); + auto usedMots = pfaedle::router::motISect(motCfg.mots, 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); + std::string motStr = pfaedle::router::getMotStr(usedMots); LOG(INFO) << "Calculating shapes for mots " << motStr; try { + pfaedle::router::FeedStops fStops = + pfaedle::router::writeMotStops(>fs[0], usedMots, cfg.shapeTripId); + + pfaedle::osm::Restrictor restr; + pfaedle::trgraph::Graph graph; + pfaedle::osm::OsmBuilder osmBuilder; + + pfaedle::osm::BBoxIdx box(BOX_PADDING); + ShapeBuilder::getGtfsBox(>fs[0], cmdCfgMots, cfg.shapeTripId, + cfg.dropShapes, &box); + + if (fStops.size()) + osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box, + cfg.gridSize, &fStops, &restr); + + // TODO(patrick): move this somewhere else + for (auto& feedStop : fStops) { + if (feedStop.second) { + feedStop.second->pl().getSI()->getGroup()->writePens( + motCfg.osmBuildOpts.trackNormzer, + motCfg.routingOpts.platformUnmatchedPen, + motCfg.routingOpts.stationDistPenFactor, + motCfg.routingOpts.nonOsmPen); + } + } + ShapeBuilder shapeBuilder(>fs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll, - cfg); + &graph, &fStops, &restr, cfg); if (cfg.writeGraph) { LOG(INFO) << "Outputting graph.json..."; @@ -276,19 +301,6 @@ int main(int argc, char** argv) { return static_cast(RetCode::SUCCESS); } -// _____________________________________________________________________________ -std::string getMotStr(const MOTs& mots) { - bool first = false; - std::string motStr; - for (const auto& mot : mots) { - if (first) motStr += ", "; - motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">"; - first = true; - } - - return motStr; -} - // _____________________________________________________________________________ std::string getFileNameMotStr(const MOTs& mots) { std::string motStr; @@ -299,18 +311,6 @@ std::string getFileNameMotStr(const MOTs& mots) { return motStr; } -// _____________________________________________________________________________ -MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) { - MOTs ret; - for (const auto& mot : mots) { - if (motCfg.mots.count(mot)) { - ret.insert(mot); - } - } - - return ret; -} - // _____________________________________________________________________________ std::vector getCfgPaths(const Config& cfg) { if (cfg.configPaths.size()) return cfg.configPaths; diff --git a/src/pfaedle/config/MotConfig.h b/src/pfaedle/config/MotConfig.h index 4a85d85..3b7ab48 100644 --- a/src/pfaedle/config/MotConfig.h +++ b/src/pfaedle/config/MotConfig.h @@ -5,21 +5,32 @@ #ifndef PFAEDLE_CONFIG_MOTCONFIG_H_ #define PFAEDLE_CONFIG_MOTCONFIG_H_ +#include +#include #include "pfaedle/osm/OsmBuilder.h" #include "pfaedle/router/Router.h" namespace pfaedle { namespace config { - struct MotConfig { router::MOTs mots; osm::OsmReadOpts osmBuildOpts; router::RoutingOpts routingOpts; + std::map unproced; }; inline bool operator==(const MotConfig& a, const MotConfig& b) { - return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts; + bool unprocedEq = a.unproced.size() == b.unproced.size(); + for (const auto& kv : a.unproced) { + if (!b.unproced.count(kv.first) || + b.unproced.find(kv.first)->second != kv.second) { + unprocedEq = false; + break; + } + } + return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts && + unprocedEq; } } // namespace config diff --git a/src/pfaedle/config/MotConfigReader.cpp b/src/pfaedle/config/MotConfigReader.cpp index c31866a..4dad419 100644 --- a/src/pfaedle/config/MotConfigReader.cpp +++ b/src/pfaedle/config/MotConfigReader.cpp @@ -35,8 +35,11 @@ void MotConfigReader::parse(const std::vector& paths) { for (const auto& sec : p.getSecs()) { MotConfig curCfg; std::string secStr = sec.first; + if (secStr.empty()) continue; + std::set procedKeys; if (p.hasKey(secStr, "osm_filter_keep")) { + procedKeys.insert("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( @@ -47,6 +50,7 @@ void MotConfigReader::parse(const std::vector& paths) { 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)) { + procedKeys.insert(name); for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) { auto fRule = getFRule(kvs); curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert( @@ -56,6 +60,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_drop")) { + procedKeys.insert("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( @@ -64,6 +69,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_max_snap_level")) { + procedKeys.insert("osm_max_snap_level"); curCfg.osmBuildOpts.maxSnapLevel = p.getInt(sec.first, "osm_max_snap_level"); } else { @@ -71,6 +77,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_nohup")) { + procedKeys.insert("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( @@ -79,6 +86,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_oneway")) { + procedKeys.insert("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( @@ -87,6 +95,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_oneway_reverse")) { + procedKeys.insert("osm_filter_oneway_reverse"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) { auto fRule = getFRule(kvs); @@ -96,6 +105,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_undirected")) { + procedKeys.insert("osm_filter_undirected"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_undirected", ' ')) { auto fRule = getFRule(kvs); @@ -105,6 +115,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_station")) { + procedKeys.insert("osm_filter_station"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_station", ' ')) { auto fRule = getFRule(kvs); @@ -114,6 +125,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_station_blocker")) { + procedKeys.insert("osm_filter_station_blocker"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) { auto fRule = getFRule(kvs); @@ -123,6 +135,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_node_positive_restriction")) { + procedKeys.insert("osm_node_positive_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) { auto fRule = getFRule(kvs); @@ -132,6 +145,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_node_negative_restriction")) { + procedKeys.insert("osm_node_negative_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) { auto fRule = getFRule(kvs); @@ -141,6 +155,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_filter_no_restriction")) { + procedKeys.insert("osm_filter_no_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) { auto fRule = getFRule(kvs); @@ -150,6 +165,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_station_name_attrs")) { + procedKeys.insert("osm_station_name_attrs"); for (const std::string& r : p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) { curCfg.osmBuildOpts.statAttrRules.nameRule.push_back( @@ -158,6 +174,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_track_number_tags")) { + procedKeys.insert("osm_track_number_tags"); for (const std::string& r : p.getStrArr(sec.first, "osm_track_number_tags", ' ')) { curCfg.osmBuildOpts.statAttrRules.platformRule.push_back( @@ -165,7 +182,16 @@ void MotConfigReader::parse(const std::vector& paths) { } } + if (p.hasKey(secStr, "osm_station_id_attrs")) { + procedKeys.insert("osm_station_id_attrs"); + for (const std::string& r : + p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) { + curCfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r)); + } + } + if (p.hasKey(secStr, "osm_edge_track_number_tags")) { + procedKeys.insert("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)); @@ -173,6 +199,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_station_group_attrs")) { + procedKeys.insert("osm_station_group_attrs"); auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' '); for (const auto& ruleStr : arr) { @@ -186,6 +213,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_line_relation_tags")) { + procedKeys.insert("osm_line_relation_tags"); auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' '); for (const auto& ruleStr : arr) { @@ -201,6 +229,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_max_snap_distance")) { + procedKeys.insert("osm_max_snap_distance"); curCfg.osmBuildOpts.maxSnapDistances = p.getDoubleArr(secStr, "osm_max_snap_distance", ','); } else { @@ -208,6 +237,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) { + procedKeys.insert("osm_max_snap_fallback_distance"); curCfg.osmBuildOpts.maxSnapFallbackHeurDistance = p.getDouble(secStr, "osm_max_snap_fallback_distance"); } else { @@ -218,6 +248,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_max_osm_station_distance")) { + procedKeys.insert("osm_max_osm_station_distance"); curCfg.osmBuildOpts.maxOsmStationDistance = p.getDouble(secStr, "osm_max_osm_station_distance"); } else { @@ -225,6 +256,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "osm_max_node_block_distance")) { + procedKeys.insert("osm_max_node_block_distance"); curCfg.osmBuildOpts.maxBlockDistance = p.getDouble(secStr, "osm_max_node_block_distance"); } else { @@ -238,6 +270,7 @@ void MotConfigReader::parse(const std::vector& paths) { std::string name = std::string("routing_lvl") + std::to_string(i) + "_fac"; if (p.hasKey(secStr, name)) { + procedKeys.insert(name); double v = p.getDouble(sec.first, name); curCfg.routingOpts.levelPunish[i] = v; } else { @@ -246,10 +279,18 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "routing_full_turn_punish")) { + procedKeys.insert("routing_full_turn_punish"); curCfg.routingOpts.fullTurnPunishFac = p.getDouble(secStr, "routing_full_turn_punish"); } + + if (p.hasKey(secStr, "routing_no_self_hops")) { + procedKeys.insert("routing_no_self_hops"); + curCfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops"); + } + if (p.hasKey(secStr, "routing_full_turn_angle")) { + procedKeys.insert("routing_full_turn_angle"); double ang = p.getDouble(secStr, "routing_full_turn_angle"); curCfg.routingOpts.fullTurnAngle = ang; curCfg.osmBuildOpts.fullTurnAngle = ang; @@ -257,39 +298,55 @@ void MotConfigReader::parse(const std::vector& paths) { curCfg.routingOpts.fullTurnAngle = 5; curCfg.osmBuildOpts.fullTurnAngle = 5; } + if (p.hasKey(secStr, "routing_snap_full_turn_angle")) { + procedKeys.insert("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")) { + procedKeys.insert("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")) { + procedKeys.insert("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")) { + procedKeys.insert("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")) { + procedKeys.insert("routing_line_unmatched_punish_fac"); curCfg.routingOpts.lineUnmatchedPunishFact = p.getDouble(secStr, "routing_line_unmatched_punish_fac"); } + if (p.hasKey(secStr, "routing_platform_unmatched_punish")) { + procedKeys.insert("routing_platform_unmatched_punish"); curCfg.routingOpts.platformUnmatchedPen = p.getDouble(secStr, "routing_platform_unmatched_punish"); } + if (p.hasKey(secStr, "routing_non_osm_station_punish")) { + procedKeys.insert("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")) { + procedKeys.insert("routing_station_distance_punish_fac"); curCfg.routingOpts.stationDistPenFactor = p.getDouble(secStr, "routing_station_distance_punish_fac"); } else { @@ -297,6 +354,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "station_normalize_chain")) { + procedKeys.insert("station_normalize_chain"); try { auto arr = p.getStrArr(secStr, "station_normalize_chain", ';'); curCfg.osmBuildOpts.statNormzer = @@ -311,6 +369,7 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "track_normalize_chain")) { + procedKeys.insert("track_normalize_chain"); try { auto arr = p.getStrArr(secStr, "track_normalize_chain", ';'); curCfg.osmBuildOpts.trackNormzer = @@ -325,19 +384,39 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "line_normalize_chain")) { + procedKeys.insert("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, + throw ParseExc(p.getVal(secStr, "line_normalize_chain").line, + p.getVal(secStr, "line_normalize_chain").pos, + "", + std::string("", + p.getVal(secStr, "line_normalize_chain").file); + } + } + + if (p.hasKey(secStr, "station_id_normalize_chain")) { + procedKeys.insert("station_id_normalize_chain"); + try { + auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';'); + curCfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr)); + } catch (const std::exception& e) { + throw ParseExc(p.getVal(secStr, "station_id_normalize_chain").line, + p.getVal(secStr, "station_id_normalize_chain").pos, "", std::string("", p.getVal(secStr, "station_normalize_chain").file); } } + for (const auto& kv : p.getKeyVals(secStr)) { + if (!procedKeys.count(kv.first)) + curCfg.unproced[kv.first] = kv.second.val; + } + bool found = false; for (auto& cfg : _cfgs) { diff --git a/src/pfaedle/gtfs/Writer.h b/src/pfaedle/gtfs/Writer.h index a11392d..deb25f8 100644 --- a/src/pfaedle/gtfs/Writer.h +++ b/src/pfaedle/gtfs/Writer.h @@ -6,6 +6,7 @@ #define PFAEDLE_GTFS_WRITER_H_ #include +#include "ad/cppgtfs/Writer.h" #include "Feed.h" namespace pfaedle { diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index e340c60..01df3d9 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -65,7 +65,6 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox, size_t gridSize, router::FeedStops* fs, Restrictor* res) { if (!bbox.size()) return; - if (!fs->size()) return; LOG(INFO) << "Reading OSM file " << path << " ... "; @@ -141,119 +140,8 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, LOG(VDEBUG) << "Writing edge geoms..."; writeGeoms(g); - { - NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), true); - EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullWebMercBox()); - - LOG(DEBUG) << "Grid size of " << sng.getXWidth() << "x" << sng.getYHeight(); - - for (double d : opts.maxSnapDistances) { - for (auto s : orphanStations) { - POINT geom = *s->pl().getGeom(); - NodePL pl = s->pl(); - pl.getSI()->setIsFromOsm(false); - 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 - // station, set is-from-osm to true - if (webMercMeterDist(geom, *n->pl().getGeom()) < - opts.maxOsmStationDistance) { - if (n->pl().getSI()) n->pl().getSI()->setIsFromOsm(true); - } - } - } - } - - std::vector notSnapped; - - for (auto& s : *fs) { - bool snapped = false; - auto pl = plFromGtfs(s.first, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - 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(); - snapped = true; - } - } - if (!snapped) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s.first->getLat() << "," << s.first->getLng() - << ") in normal run, trying again later in orphan mode."; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } - 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 (auto& s : notSnapped) { - bool snapped = false; - auto pl = plFromGtfs(s, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - 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(); - snapped = true; - } - } - if (!snapped) { - // 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); - (*fs)[s] = dummyNode; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } else { - // only warn if it is contained in the BBOX for this graph - LOG(WARN) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - } - } - } - } + LOG(VDEBUG) << "Snapping stations..."; + snapStats(opts, g, bbox, gridSize, fs, res, orphanStations); LOG(VDEBUG) << "Deleting orphan nodes..."; deleteOrphNds(g); @@ -534,12 +422,17 @@ void OsmBuilder::readWriteWays(xml::File* i, util::xml::XmlWriter* o, } // _____________________________________________________________________________ -NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) const { +NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { NodePL ret( util::geo::latLngToWebMerc(s->getLat(), s->getLng()), StatInfo(ops.statNormzer(s->getName()), ops.trackNormzer(s->getPlatformCode()), false)); +#ifdef PFAEDLE_STATION_IDS + // debug feature, store station id from GTFS + ret.getSI()->setId(s->getId()); +#endif + if (s->getParentStation()) { ret.getSI()->addAltName(ops.statNormzer(s->getParentStation()->getName())); } @@ -1109,6 +1002,11 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, auto ret = StatInfo(names[0], platform, true); +#ifdef PFAEDLE_STATION_IDS + ret.setId(getAttrByFirstMatch(ops.statAttrRules.idRule, nid, m, nodeRels, + rels, ops.idNormzer)); +#endif + for (size_t i = 1; i < names.size(); i++) ret.addAltName(names[i]); bool groupFound = false; @@ -1151,26 +1049,17 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, } // _____________________________________________________________________________ -double OsmBuilder::dist(const Node* a, const Node* b) const { +double OsmBuilder::dist(const Node* a, const Node* b) { return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); } // _____________________________________________________________________________ -double OsmBuilder::webMercDistFactor(const POINT& a) const { - // euclidean distance on web mercator is in meters on equator, - // and proportional to cos(lat) in both y directions - - double lat = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965; - return cos(lat); -} - -// _____________________________________________________________________________ -double OsmBuilder::webMercDist(const Node* a, const Node* b) const { +double OsmBuilder::webMercDist(const Node* a, const Node* b) { return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); } // _____________________________________________________________________________ -void OsmBuilder::writeGeoms(Graph* g) const { +void OsmBuilder::writeGeoms(Graph* g) { for (auto* n : *g->getNds()) { for (auto* e : n->getAdjListOut()) { e->pl().addPoint(*e->getFrom()->pl().getGeom()); @@ -1181,35 +1070,47 @@ void OsmBuilder::writeGeoms(Graph* g) const { } // _____________________________________________________________________________ -void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) const { +void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { + double METER = 1; for (auto* n : *g->getNds()) { if (n->getInDeg() + n->getOutDeg() == 1) { - // get all nodes in a 1 meter distance + // get all nodes in distance std::set ret; - ng->get(util::geo::pad(util::geo::getBoundingBox(*n->pl().getGeom()), 1), + double distor = util::geo::webMercDistFactor(*n->pl().getGeom()); + ng->get(util::geo::pad(util::geo::getBoundingBox(*n->pl().getGeom()), + METER / distor), &ret); for (auto* nb : ret) { if (nb != n && (nb->getInDeg() + nb->getOutDeg()) == 1 && - webMercDist(nb, n) <= 1.0 && !nb->pl().getSI() && - !n->pl().getSI()) { - Node* otherN; - if (nb->getOutDeg()) - otherN = (*nb->getAdjListOut().begin())->getOtherNd(nb); - else - otherN = (*nb->getAdjListIn().begin())->getOtherNd(nb); - LINE l; - l.push_back(*otherN->pl().getGeom()); - l.push_back(*n->pl().getGeom()); + webMercDist(nb, n) <= METER / distor) { + // special case: both node are non-stations, move + // the end point nb to n and delete nb + if (!nb->pl().getSI() && !n->pl().getSI()) { + Node* otherN; + if (nb->getOutDeg()) + otherN = (*nb->getAdjListOut().begin())->getOtherNd(nb); + else + otherN = (*nb->getAdjListIn().begin())->getOtherNd(nb); + LINE l; + l.push_back(*otherN->pl().getGeom()); + l.push_back(*n->pl().getGeom()); - Edge* e; - if (nb->getOutDeg()) - e = g->addEdg(otherN, n, (*nb->getAdjListOut().begin())->pl()); - else - e = g->addEdg(otherN, n, (*nb->getAdjListIn().begin())->pl()); - if (e) { - *e->pl().getGeom() = l; - g->delNd(nb); - ng->remove(nb); + Edge* e; + if (nb->getOutDeg()) + e = g->addEdg(otherN, n, (*nb->getAdjListOut().begin())->pl()); + else + e = g->addEdg(otherN, n, (*nb->getAdjListIn().begin())->pl()); + if (e) { + *e->pl().getGeom() = l; + g->delNd(nb); + ng->remove(nb); + } + } else { + // if one of the nodes is a station, just add an edge between them + if (nb->getOutDeg()) + g->addEdg(n, nb, (*nb->getAdjListOut().begin())->pl()); + else + g->addEdg(n, nb, (*nb->getAdjListIn().begin())->pl()); } } } @@ -1219,7 +1120,7 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) const { // _____________________________________________________________________________ EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size, - const BOX& webMercBox) const { + const BOX& webMercBox) { EdgeGrid ret(size, size, webMercBox, false); for (auto* n : *g->getNds()) { for (auto* e : n->getAdjListOut()) { @@ -1232,7 +1133,7 @@ EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size, // _____________________________________________________________________________ NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, - bool which) const { + bool which) { NodeGrid ret(size, size, webMercBox, false); for (auto* n : *g->getNds()) { if (!which && n->getInDeg() + n->getOutDeg() == 1) @@ -1246,7 +1147,7 @@ NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, // _____________________________________________________________________________ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, - const SearchFunc& sfunc) const { + const SearchFunc& sfunc) { // shortcuts double dFrom = webMercMeterDist(*e->getFrom()->pl().getGeom(), p); double dTo = webMercMeterDist(*e->getTo()->pl().getGeom(), p); @@ -1310,23 +1211,22 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, // _____________________________________________________________________________ bool OsmBuilder::isBlocked(const Edge* e, const StatInfo* si, const POINT& p, - double maxD, int maxFullTurns, - double minAngle) const { + double maxD, int maxFullTurns, double minAngle) { return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, BlockSearch()); } // _____________________________________________________________________________ Node* OsmBuilder::eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, - bool orphanSnap) const { + bool orphanSnap) { return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, EqSearch(orphanSnap)); } // _____________________________________________________________________________ void OsmBuilder::getEdgCands(const POINT& geom, EdgeCandPQ* ret, EdgeGrid* eg, - double d) const { - double distor = webMercDistFactor(geom); + double d) { + double distor = util::geo::webMercDistFactor(geom); std::set neighs; BOX box = util::geo::pad(util::geo::getBoundingBox(geom), d / distor); eg->get(box, &neighs); @@ -1343,9 +1243,9 @@ void OsmBuilder::getEdgCands(const POINT& geom, EdgeCandPQ* ret, EdgeGrid* eg, // _____________________________________________________________________________ std::set OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng, - double d) const { + double d) { std::set ret; - double distor = webMercDistFactor(*s.getGeom()); + double distor = util::geo::webMercDistFactor(*s.getGeom()); std::set neighs; BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); ng->get(box, &neighs); @@ -1361,8 +1261,8 @@ std::set OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng, } // _____________________________________________________________________________ -Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const { - double distor = webMercDistFactor(*s.getGeom()); +Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) { + double distor = util::geo::webMercDistFactor(*s.getGeom()); std::set neighs; BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); ng->get(box, &neighs); @@ -1387,7 +1287,7 @@ Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const { std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, const OsmReadOpts& opts, Restrictor* restor, bool surrHeur, - bool orphSnap, double d) const { + bool orphSnap, double d) { assert(s->getSI()); std::set ret; @@ -1467,6 +1367,13 @@ std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, ret.insert(n); } } else { + // if the snapped station is very near to the original OSM station + // write additional info from this snap station to the equivalent stat + if (webMercMeterDist(*s->getGeom(), *eq->pl().getGeom()) < + opts.maxOsmStationDistance) { + if (eq->pl().getSI()->getTrack().empty()) + eq->pl().getSI()->setTrack(s->getSI()->getTrack()); + } ret.insert(eq); } } @@ -1475,7 +1382,7 @@ std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, } // _____________________________________________________________________________ -StatGroup* OsmBuilder::groupStats(const NodeSet& s) const { +StatGroup* OsmBuilder::groupStats(const NodeSet& s) { if (!s.size()) return 0; // reference group StatGroup* ret = new StatGroup(); @@ -1664,10 +1571,19 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, sets[2].insert(i.attr); } } + + for (const auto& i : opts.statAttrRules.idRule) { + if (i.relRule.kv.first.empty()) { + sets[0].insert(i.attr); + } else { + sets[2].insert(i.relRule.kv.first); + sets[2].insert(i.attr); + } + } } // _____________________________________________________________________________ -void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const { +void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) { size_t ROUNDS = 3; for (size_t c = 0; c < ROUNDS; c++) { for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { @@ -1692,11 +1608,10 @@ void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const { } // _____________________________________________________________________________ -void OsmBuilder::deleteOrphNds(Graph* g) const { +void OsmBuilder::deleteOrphNds(Graph* g) { for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { if ((*i)->getInDeg() + (*i)->getOutDeg() == 0 && - !((*i)->pl().getSI() && (*i)->pl().getSI()->getGroup() && - (*i)->pl().getSI()->getGroup()->getStops().size())) { + !((*i)->pl().getSI() && (*i)->pl().getSI()->getGroup())) { i = g->delNd(i); // TODO(patrick): maybe delete from node grid? } else { @@ -1706,7 +1621,7 @@ void OsmBuilder::deleteOrphNds(Graph* g) const { } // _____________________________________________________________________________ -bool OsmBuilder::edgesSim(const Edge* a, const Edge* b) const { +bool OsmBuilder::edgesSim(const Edge* a, const Edge* b) { if (static_cast(a->pl().oneWay()) ^ static_cast(b->pl().oneWay())) return false; if (a->pl().lvl() != b->pl().lvl()) return false; @@ -1721,7 +1636,7 @@ bool OsmBuilder::edgesSim(const Edge* a, const Edge* b) const { } // _____________________________________________________________________________ -const EdgePL& OsmBuilder::mergeEdgePL(Edge* a, Edge* b) const { +const EdgePL& OsmBuilder::mergeEdgePL(Edge* a, Edge* b) { const Node* n = 0; if (a->getFrom() == b->getFrom()) n = a->getFrom(); @@ -1760,7 +1675,7 @@ const EdgePL& OsmBuilder::mergeEdgePL(Edge* a, Edge* b) const { } // _____________________________________________________________________________ -void OsmBuilder::collapseEdges(Graph* g) const { +void OsmBuilder::collapseEdges(Graph* g) { for (auto* n : *g->getNds()) { if (n->getOutDeg() + n->getInDeg() != 2 || n->pl().getSI()) continue; @@ -1795,7 +1710,7 @@ void OsmBuilder::collapseEdges(Graph* g) const { } // _____________________________________________________________________________ -void OsmBuilder::simplifyGeoms(Graph* g) const { +void OsmBuilder::simplifyGeoms(Graph* g) { for (auto* n : *g->getNds()) { for (auto* e : n->getAdjListOut()) { (*e->pl().getGeom()) = util::geo::simplify(*e->pl().getGeom(), 0.5); @@ -1804,7 +1719,7 @@ void OsmBuilder::simplifyGeoms(Graph* g) const { } // _____________________________________________________________________________ -uint32_t OsmBuilder::writeComps(Graph* g) const { +uint32_t OsmBuilder::writeComps(Graph* g) { Component* comp = new Component{7}; uint32_t numC = 0; @@ -1843,7 +1758,7 @@ uint32_t OsmBuilder::writeComps(Graph* g) const { } // _____________________________________________________________________________ -void OsmBuilder::writeEdgeTracks(const EdgTracks& tracks) const { +void OsmBuilder::writeEdgeTracks(const EdgTracks& tracks) { for (const auto& tr : tracks) { if (tr.first->getTo()->pl().getSI() && tr.first->getTo()->pl().getSI()->getTrack().empty()) { @@ -1857,7 +1772,7 @@ void OsmBuilder::writeEdgeTracks(const EdgTracks& tracks) const { } // _____________________________________________________________________________ -void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) const { +void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) { for (auto* n : *g->getNds()) { for (auto* e : n->getAdjListOut()) { if (g->getEdg(e->getTo(), e->getFrom())) continue; @@ -1868,7 +1783,7 @@ void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) const { } // _____________________________________________________________________________ -void OsmBuilder::writeSelfEdgs(Graph* g) const { +void OsmBuilder::writeSelfEdgs(Graph* g) { for (auto* n : *g->getNds()) { if (n->pl().getSI() && n->getAdjListOut().size() == 0) { g->addEdg(n, n); @@ -1877,7 +1792,7 @@ void OsmBuilder::writeSelfEdgs(Graph* g) const { } // _____________________________________________________________________________ -bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) const { +bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) { if (n->getInDeg() + n->getOutDeg() != 1) return false; const trgraph::Edge* e = 0; @@ -1915,3 +1830,123 @@ bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) const { return false; } + +// _____________________________________________________________________________ +void OsmBuilder::snapStats(const OsmReadOpts& opts, Graph* g, + const BBoxIdx& bbox, size_t gridSize, + router::FeedStops* fs, Restrictor* res, + const NodeSet& orphanStations) { + NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), true); + EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullWebMercBox()); + + LOG(DEBUG) << "Grid size of " << sng.getXWidth() << "x" << sng.getYHeight(); + + for (double d : opts.maxSnapDistances) { + for (auto s : orphanStations) { + POINT geom = *s->pl().getGeom(); + NodePL pl = s->pl(); + pl.getSI()->setIsFromOsm(false); + 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 + // station, set is-from-osm to true + if (webMercMeterDist(geom, *n->pl().getGeom()) < + opts.maxOsmStationDistance) { + if (n->pl().getSI()) n->pl().getSI()->setIsFromOsm(true); + } + } + } + } + + if (!fs) return; + + std::vector notSnapped; + + for (auto& s : *fs) { + bool snapped = false; + auto pl = plFromGtfs(s.first, opts); + for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { + double d = opts.maxSnapDistances[i]; + + 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(); + snapped = true; + } + } + if (!snapped) { + LOG(VDEBUG) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s.first->getLat() << "," << s.first->getLng() + << ") in normal run, trying again later in orphan mode."; + if (!bbox.contains(*pl.getGeom())) { + LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() + << "' does not lie within the bounds for this graph and " + "may be a stray station"; + } + 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 (auto& s : notSnapped) { + bool snapped = false; + auto pl = plFromGtfs(s, opts); + for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { + double d = opts.maxSnapDistances[i]; + + 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(); + snapped = true; + } + } + if (!snapped) { + // 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); + (*fs)[s] = dummyNode; + if (!bbox.contains(*pl.getGeom())) { + LOG(VDEBUG) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s->getLat() << "," << s->getLng() << ")"; + LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() + << "' does not lie within the bounds for this graph and " + "may be a stray station"; + } else { + // only warn if it is contained in the BBOX for this graph + LOG(WARN) << "Could not snap station " + << "(" << pl.getSI()->getName() << ")" + << " (" << s->getLat() << "," << s->getLng() << ")"; + } + } + } +} diff --git a/src/pfaedle/osm/OsmBuilder.h b/src/pfaedle/osm/OsmBuilder.h index 2a2efa2..fdcb764 100644 --- a/src/pfaedle/osm/OsmBuilder.h +++ b/src/pfaedle/osm/OsmBuilder.h @@ -4,7 +4,6 @@ #ifndef PFAEDLE_OSM_OSMBUILDER_H_ #define PFAEDLE_OSM_OSMBUILDER_H_ - #include #include #include @@ -168,65 +167,70 @@ class OsmBuilder { OsmRel nextRel(xml::File* xml, const OsmFilter& filter, const AttrKeySet& keepAttrs) const; + protected: Nullable getStatInfo(Node* node, osmid nid, const POINT& pos, const AttrMap& m, StAttrGroups* groups, const RelMap& nodeRels, const RelLst& rels, const OsmReadOpts& ops) const; - void writeGeoms(Graph* g) const; - void deleteOrphNds(Graph* g) const; - void deleteOrphEdgs(Graph* g, const 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; + static void snapStats(const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox, + size_t gridSize, router::FeedStops* fs, Restrictor* res, + const NodeSet& orphanStations); + static void writeGeoms(Graph* g); + static void deleteOrphNds(Graph* g); + static void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts); + static double dist(const Node* a, const Node* b); + static double webMercDist(const Node* a, const Node* b); - NodeGrid buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, - bool which) const; + static NodeGrid buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, + bool which); - EdgeGrid buildEdgeIdx(Graph* g, size_t size, const BOX& webMercBox) const; + static EdgeGrid buildEdgeIdx(Graph* g, size_t size, const BOX& webMercBox); - void fixGaps(Graph* g, NodeGrid* ng) const; - void collapseEdges(Graph* g) const; - void writeODirEdgs(Graph* g, Restrictor* restor) const; - void writeSelfEdgs(Graph* g) const; - void writeEdgeTracks(const EdgTracks& tracks) const; - void simplifyGeoms(Graph* g) const; - uint32_t writeComps(Graph* g) const; - bool edgesSim(const Edge* a, const Edge* b) const; - const EdgePL& mergeEdgePL(Edge* a, Edge* b) const; - void getEdgCands(const POINT& s, EdgeCandPQ* ret, EdgeGrid* eg, - double d) const; + static void fixGaps(Graph* g, NodeGrid* ng); + static void collapseEdges(Graph* g); + static void writeODirEdgs(Graph* g, Restrictor* restor); + static void writeSelfEdgs(Graph* g); + static void writeEdgeTracks(const EdgTracks& tracks); + static void simplifyGeoms(Graph* g); + static uint32_t writeComps(Graph* g); + static bool edgesSim(const Edge* a, const Edge* b); + static const EdgePL& mergeEdgePL(Edge* a, Edge* b); + static void getEdgCands(const POINT& s, EdgeCandPQ* ret, EdgeGrid* eg, + double d); - std::set getMatchingNds(const NodePL& s, NodeGrid* ng, double d) const; + static std::set getMatchingNds(const NodePL& s, NodeGrid* ng, + double d); - Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const; + static Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d); - NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, - const OsmReadOpts& opts, Restrictor* restor, bool surHeur, - bool orphSnap, double maxD) const; + static NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, + const OsmReadOpts& opts, Restrictor* restor, + bool surHeur, bool orphSnap, double maxD); // 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, - bool orph) const; + static Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, + double maxD, int maxFullTurns, double maxAng, + bool orph); - Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p, - double maxD, int maxFullTurns, double minAngle, - const SearchFunc& sfunc) const; + static Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p, + double maxD, int maxFullTurns, double minAngle, + const SearchFunc& sfunc); - bool isBlocked(const Edge* e, const StatInfo* si, const POINT& p, double maxD, - int maxFullTurns, double minAngle) const; + static bool isBlocked(const Edge* e, const StatInfo* si, const POINT& p, + double maxD, int maxFullTurns, double minAngle); + static bool keepFullTurn(const trgraph::Node* n, double ang); - StatGroup* groupStats(const NodeSet& s) const; + static StatGroup* groupStats(const NodeSet& s); + + static NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops); std::vector getLines(const std::vector& edgeRels, const RelLst& rels, const OsmReadOpts& ops); - NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops) const; - void getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const; void skipUntil(xml::File* xml, const std::string& s) const; @@ -238,6 +242,7 @@ class OsmBuilder { const AttrMap& attrs, const RelMap& entRels, const RelLst& rels, const Normalizer& norm) const; + std::vector getAttrMatchRanked(const DeepAttrLst& rule, osmid id, const AttrMap& attrs, const RelMap& entRels, @@ -249,8 +254,6 @@ class OsmBuilder { bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const; - bool keepFullTurn(const trgraph::Node* n, double ang) const; - std::map _lines; std::map _relLines; }; diff --git a/src/pfaedle/osm/OsmReadOpts.h b/src/pfaedle/osm/OsmReadOpts.h index 12e6d72..47d78ae 100644 --- a/src/pfaedle/osm/OsmReadOpts.h +++ b/src/pfaedle/osm/OsmReadOpts.h @@ -87,6 +87,7 @@ inline bool operator==(const RelLineRules& a, const RelLineRules& b) { struct StationAttrRules { DeepAttrLst nameRule; DeepAttrLst platformRule; + DeepAttrLst idRule; }; inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) { @@ -125,6 +126,7 @@ struct OsmReadOpts { trgraph::Normalizer statNormzer; trgraph::Normalizer lineNormzer; trgraph::Normalizer trackNormzer; + trgraph::Normalizer idNormzer; RelLineRules relLinerules; StationAttrRules statAttrRules; diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index 07bf013..4380128 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -12,6 +12,8 @@ #include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Route.h" #include "pfaedle/trgraph/Graph.h" +#include "pfaedle/gtfs/Feed.h" +#include "util/Nullable.h" using ad::cppgtfs::gtfs::Route; using ad::cppgtfs::gtfs::Stop; @@ -24,6 +26,11 @@ struct NodeCand { double pen; }; +struct EdgeCand { + trgraph::Edge* e; + double pen; +}; + struct RoutingOpts { RoutingOpts() : fullTurnPunishFac(2000), @@ -33,7 +40,9 @@ struct RoutingOpts { oneWayEdgePunish(0), lineUnmatchedPunishFact(0.5), platformUnmatchedPen(0), - stationDistPenFactor(0) {} + stationDistPenFactor(0), + popReachEdge(true), + noSelfHops(true) {} double fullTurnPunishFac; double fullTurnAngle; double passThruStationsPunish; @@ -44,6 +53,8 @@ struct RoutingOpts { double stationDistPenFactor; double nonOsmPen; double levelPunish[8]; + bool popReachEdge; + bool noSelfHops; }; inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { @@ -63,7 +74,8 @@ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 && fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 && fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 && - fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01; + fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01 && + a.popReachEdge == b.popReachEdge && a.noSelfHops == b.noSelfHops; } struct EdgeCost { @@ -124,6 +136,9 @@ typedef std::unordered_map FeedStops; typedef std::vector NodeCandGroup; typedef std::vector NodeCandRoute; +typedef std::vector EdgeCandGroup; +typedef std::vector EdgeCandRoute; + typedef std::vector EdgeList; typedef std::vector NodeList; @@ -136,6 +151,38 @@ struct EdgeListHop { typedef std::vector EdgeListHops; typedef std::set MOTs; + +inline MOTs motISect(const MOTs& a, const MOTs& b) { + MOTs ret; + for (auto mot : a) + if (b.count(mot)) ret.insert(mot); + return ret; +} + +inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed, + const MOTs mots, + const std::string& tid) { + pfaedle::router::FeedStops ret; + for (auto t : feed->getTrips()) { + if (!tid.empty() && t.getId() != tid) continue; + if (mots.count(t.getRoute()->getType())) { + for (auto st : t.getStopTimes()) ret[st.getStop()] = 0; + } + } + return ret; +} + +inline std::string getMotStr(const MOTs& mots) { + bool first = false; + std::string motStr; + for (const auto& mot : mots) { + if (first) motStr += ", "; + motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">"; + first = true; + } + + return motStr; +} } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp index 703825f..4835653 100644 --- a/src/pfaedle/router/Router.cpp +++ b/src/pfaedle/router/Router.cpp @@ -107,11 +107,14 @@ EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n, // _____________________________________________________________________________ double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const { + if (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && + _rAttrs.fromString.empty()) + return 0; double best = 1; for (const auto* l : e.getLines()) { double cur = _rAttrs.simi(l); - if (cur < 0.0001) return cur; + if (cur < 0.0001) return 0; if (cur < best) best = cur; } @@ -206,10 +209,11 @@ Router::~Router() { } // _____________________________________________________________________________ -bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const { +bool Router::compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const { for (auto n1 : a) { for (auto n2 : b) { - if (n1.nd->pl().getComp() == n2.nd->pl().getComp()) return true; + if (n1.e->getFrom()->pl().getComp() == n2.e->getFrom()->pl().getComp()) + return true; } } @@ -217,7 +221,7 @@ bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const { } // _____________________________________________________________________________ -HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, +HopBand Router::getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const osm::Restrictor& rest) const { assert(a.size()); @@ -226,8 +230,8 @@ HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, double pend = 0; for (size_t i = 0; i < a.size(); i++) { for (size_t j = 0; j < b.size(); j++) { - double d = - webMercMeterDist(*a[i].nd->pl().getGeom(), *b[j].nd->pl().getGeom()); + double d = webMercMeterDist(*a[i].e->getFrom()->pl().getGeom(), + *b[j].e->getFrom()->pl().getGeom()); if (d > pend) pend = d; } } @@ -236,23 +240,18 @@ HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, const trgraph::StatGroup* tgGrpTo = 0; - if (b.begin()->nd->pl().getSI()) - tgGrpTo = b.begin()->nd->pl().getSI()->getGroup(); + if (b.begin()->e->getFrom()->pl().getSI()) + tgGrpTo = b.begin()->e->getFrom()->pl().getSI()->getGroup(); CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50); std::set from, to; - // TODO(patrick): test if the two sets share a common connected component - - for (auto n : a) - from.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end()); - - for (auto n : b) - to.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end()); + for (auto e : a) from.insert(e.e); + for (auto e : b) to.insert(e.e); LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size() - << " candidates"; + << " edge candidates"; EdgeList el; EdgeCost ret = costF.inf(); @@ -282,7 +281,8 @@ HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, } // TODO(patrick): derive the punish level here automatically - double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3; + double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3 + + rOpts.fullTurnPunishFac + rOpts.platformUnmatchedPen; double minD = ret.getValue(); LOG(VDEBUG) << "Pilot run: min distance between two groups is " @@ -379,7 +379,15 @@ EdgeListHops Router::routeGreedy2(const NodeCandRoute& route, } // _____________________________________________________________________________ -EdgeListHops Router::route(const NodeCandRoute& route, +EdgeListHops Router::route(const EdgeCandRoute& route, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest) const { + router::Graph cg; + return Router::route(route, rAttrs, rOpts, rest, &cg); +} + +// _____________________________________________________________________________ +EdgeListHops Router::route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const osm::Restrictor& rest, router::Graph* cgraph) const { @@ -393,15 +401,14 @@ EdgeListHops Router::route(const NodeCandRoute& route, CombNodeMap nextNodes; for (size_t i = 0; i < route[0].size(); i++) { - for (const auto* e : route[0][i].nd->getAdjListOut()) { - // we can be sure that each edge is exactly assigned to only one - // node because the transitgraph is directed - nodes[e] = cgraph->addNd(route[0][i].nd); - cgraph->addEdg(source, nodes[e]) - ->pl() - .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - route[0][i].pen, 0)); - } + auto e = route[0][i].e; + // we can be sure that each edge is exactly assigned to only one + // node because the transitgraph is directed + nodes[e] = cgraph->addNd(route[0][i].e->getFrom()); + cgraph->addEdg(source, nodes[e]) + ->pl() + .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + route[0][i].pen, 0)); } size_t iters = EDijkstra::ITERS; @@ -412,14 +419,11 @@ EdgeListHops Router::route(const NodeCandRoute& route, HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest); const trgraph::StatGroup* tgGrp = 0; - if (route[i + 1].begin()->nd->pl().getSI()) - tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup(); + if (route[i + 1].begin()->e->getFrom()->pl().getSI()) + tgGrp = route[i + 1].begin()->e->getFrom()->pl().getSI()->getGroup(); std::set froms; - for (const auto& fr : route[i]) { - froms.insert(fr.nd->getAdjListOut().begin(), - fr.nd->getAdjListOut().end()); - } + for (const auto& fr : route[i]) froms.insert(fr.e); for (auto eFr : froms) { router::Node* cNodeFr = nodes.find(eFr)->second; @@ -433,24 +437,22 @@ EdgeListHops Router::route(const NodeCandRoute& route, assert(route[i + 1].size()); for (const auto& to : route[i + 1]) { - assert(to.nd->getAdjListOut().size()); - for (auto eTo : to.nd->getAdjListOut()) { - tos.insert(eTo); - if (!nextNodes.count(eTo)) nextNodes[eTo] = cgraph->addNd(to.nd); - if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink); + auto eTo = to.e; + tos.insert(eTo); + if (!nextNodes.count(eTo)) + nextNodes[eTo] = cgraph->addNd(to.e->getFrom()); + if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink); - auto* ce = cgraph->addEdg(cNodeFr, nextNodes[eTo]); - edges[eTo] = ce; - pens[eTo] = to.pen; + edges[eTo] = cgraph->addEdg(cNodeFr, nextNodes[eTo]); + pens[eTo] = to.pen; - edgeLists[eTo] = ce->pl().getEdges(); - ce->pl().setStartNode(eFr->getFrom()); - // for debugging - ce->pl().setStartEdge(eFr); - ce->pl().setEndNode(to.nd); - // for debugging - ce->pl().setEndEdge(eTo); - } + edgeLists[eTo] = edges[eTo]->pl().getEdges(); + edges[eTo]->pl().setStartNode(eFr->getFrom()); + // for debugging + edges[eTo]->pl().setStartEdge(eFr); + edges[eTo]->pl().setEndNode(to.e->getFrom()); + // for debugging + edges[eTo]->pl().setEndEdge(eTo); } size_t iters = EDijkstra::ITERS; @@ -475,7 +477,7 @@ EdgeListHops Router::route(const NodeCandRoute& route, EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) + costs[kv.first]); - if (kv.second->pl().getEdges()->size()) { + if (rOpts.popReachEdge && kv.second->pl().getEdges()->size()) { if (kv.second->pl().getEdges() && kv.second->pl().getEdges()->size()) { // the reach edge is included, but we dont want it in the geometry @@ -516,6 +518,30 @@ EdgeListHops Router::route(const NodeCandRoute& route, return ret; } +// _____________________________________________________________________________ +EdgeListHops Router::route(const NodeCandRoute& route, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest) const { + router::Graph cg; + return Router::route(route, rAttrs, rOpts, rest, &cg); +} + +// _____________________________________________________________________________ +EdgeListHops Router::route(const NodeCandRoute& route, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest, + router::Graph* cgraph) const { + EdgeCandRoute r; + for (auto& nCands : route) { + r.emplace_back(); + for (auto n : nCands) + for (auto* e : n.nd->getAdjListOut()) + r.back().push_back(EdgeCand{e, n.pen}); + } + + return Router::route(r, rAttrs, rOpts, rest, cgraph); +} + // _____________________________________________________________________________ void Router::hops(trgraph::Edge* from, const std::set& froms, const std::set tos, @@ -533,7 +559,7 @@ void Router::hops(trgraph::Edge* from, const std::set& froms, for (auto e : cached) { // shortcut: if the nodes lie in two different connected components, // the distance between them is trivially infinite - if (e == from || e->getFrom() == from->getFrom() || + if ((rOpts.noSelfHops && (e == from || e->getFrom() == from->getFrom())) || from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() || e->pl().oneWay() == 2 || from->pl().oneWay() == 2) { (*rCosts)[e] = cost.inf(); diff --git a/src/pfaedle/router/Router.h b/src/pfaedle/router/Router.h index 76003d5..cdd72f4 100644 --- a/src/pfaedle/router/Router.h +++ b/src/pfaedle/router/Router.h @@ -5,23 +5,23 @@ #ifndef PFAEDLE_ROUTER_ROUTER_H_ #define PFAEDLE_ROUTER_ROUTER_H_ -#include +#include #include +#include +#include +#include #include #include #include -#include -#include -#include +#include "pfaedle/Def.h" #include "pfaedle/osm/Restrictor.h" #include "pfaedle/router/Graph.h" #include "pfaedle/router/Misc.h" #include "pfaedle/router/RoutingAttrs.h" #include "pfaedle/trgraph/Graph.h" +#include "util/geo/Geo.h" #include "util/graph/Dijkstra.h" #include "util/graph/EDijkstra.h" -#include "util/geo/Geo.h" -#include "pfaedle/Def.h" using util::graph::EDijkstra; using util::graph::Dijkstra; @@ -53,14 +53,12 @@ struct CostFunc : _rAttrs(rAttrs), _rOpts(rOpts), _res(res), - _max(max), _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _max, 0) {} + _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {} const RoutingAttrs& _rAttrs; const RoutingOpts& _rOpts; const osm::Restrictor& _res; - double _max; const trgraph::StatGroup* _tgGrp; EdgeCost _inf; @@ -142,6 +140,17 @@ class Router { // Find the most likely path through the graph for a node candidate route. EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, + const osm::Restrictor& rest) const; + EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, const osm::Restrictor& rest, + router::Graph* cgraph) const; + + // Find the most likely path through the graph for an edge candidate route. + EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, + const osm::Restrictor& rest) const; + EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const osm::Restrictor& rest, router::Graph* cgraph) const; @@ -164,7 +173,7 @@ class Router { private: mutable std::vector _cache; bool _caching; - HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, + HopBand getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const osm::Restrictor& rest) const; @@ -187,7 +196,7 @@ class Router { void nestedCache(const EdgeList* el, const std::set& froms, const CostFunc& cost, const RoutingAttrs& rAttrs) const; - bool compConned(const NodeCandGroup& a, const NodeCandGroup& b) const; + bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const; }; } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/RoutingAttrs.h b/src/pfaedle/router/RoutingAttrs.h index 05aa5ed..11a5cdb 100644 --- a/src/pfaedle/router/RoutingAttrs.h +++ b/src/pfaedle/router/RoutingAttrs.h @@ -22,19 +22,22 @@ struct RoutingAttrs { mutable std::map _simiCache; + // carfull: lower return value = higher similarity double simi(const TransitEdgeLine* line) const { auto i = _simiCache.find(line); if (i != _simiCache.end()) return i->second; double cur = 1; - if (router::lineSimi(line->shortName, shortName) > 0.5) cur -= 0.33; + if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5) + cur -= 0.333333333; - if (line->toStr.empty() || router::statSimi(line->toStr, toString) > 0.5) - cur -= 0.33; + if (toString.empty() || line->toStr.empty() || + router::statSimi(line->toStr, toString) > 0.5) + cur -= 0.333333333; - if (line->fromStr.empty() || + if (fromString.empty() || line->fromStr.empty() || router::statSimi(line->fromStr, fromString) > 0.5) - cur -= 0.33; + cur -= 0.333333333; _simiCache[line] = cur; diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index 988b7ad..67073b8 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -54,43 +54,27 @@ using ad::cppgtfs::gtfs::ShapePoint; // _____________________________________________________________________________ ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots, const config::MotConfig& motCfg, - eval::Collector* ecoll, const config::Config& cfg) + eval::Collector* ecoll, pfaedle::trgraph::Graph* g, + router::FeedStops* fStops, osm::Restrictor* restr, + const config::Config& cfg) : _feed(feed), _evalFeed(evalFeed), _mots(mots), _motCfg(motCfg), _ecoll(ecoll), _cfg(cfg), + _g(g), _crouter(omp_get_num_procs(), cfg.useCaching), - _curShpCnt(0) { + _stops(fStops), + _curShpCnt(0), + _restr(restr) { _numThreads = _crouter.getCacheNumber(); - writeMotStops(); - - buildGraph(); } -// _____________________________________________________________________________ -void ShapeBuilder::writeMotStops() { - for (auto t : _feed->getTrips()) { - 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; - } - } - } -} - -// _____________________________________________________________________________ -FeedStops* ShapeBuilder::getFeedStops() { return &_stops; } - // _____________________________________________________________________________ const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const { - if (_stops.find(s) == _stops.end() || _stops.at(s) == 0) { - return _emptyNCG; - } - return _stops.at(s)->pl().getSI()->getGroup()->getNodeCands(s); + if (_stops->find(s) == _stops->end() || _stops->at(s) == 0) return _emptyNCG; + return _stops->at(s)->pl().getSI()->getGroup()->getNodeCands(s); } // _____________________________________________________________________________ @@ -138,7 +122,7 @@ EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr, if (_cfg.solveMethod == "global") { const router::EdgeListHops& ret = - _crouter.route(ncr, rAttrs, _motCfg.routingOpts, _restr, &g); + _crouter.route(ncr, rAttrs, _motCfg.routingOpts, *_restr, &g); // write combination graph if (!_cfg.shapeTripId.empty() && _cfg.writeCombGraph) { @@ -150,9 +134,9 @@ EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr, return ret; } else if (_cfg.solveMethod == "greedy") { - return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, _restr); + return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, *_restr); } else if (_cfg.solveMethod == "greedy2") { - return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, _restr); + return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, *_restr); } else { LOG(ERROR) << "Unknown solution method " << _cfg.solveMethod; exit(1); @@ -228,15 +212,6 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { LOG(INFO) << "@ " << j << " / " << clusters.size() << " (" << (static_cast((j * 1.0) / clusters.size() * 100)) << "%, " << (EDijkstra::ITERS - oiters) << " iters, " - /** - TODO: this is actually misleading. We are counting the - Dijkstra iterations, but measuring them against - the total running time (including all overhead + HMM solve) - << tput " - << (static_cast(EDijkstra::ITERS - oiters)) / - TOOK(t1, TIME()) - << " iters/ms, " - **/ << "matching " << (10.0 / (TOOK(t1, TIME()) / 1000)) << " trips/sec)"; @@ -259,13 +234,13 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { const ad::cppgtfs::gtfs::Shape& shp = getGtfsShape(cshp, clusters[i][0], &distances); - LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations."; + LOG(VDEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations."; iters = EDijkstra::ITERS; totNumTrips += clusters[i].size(); for (auto t : clusters[i]) { - if (_cfg.evaluate) { + if (_cfg.evaluate && _evalFeed && _ecoll) { _ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp, distances); } @@ -472,27 +447,6 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, } } -// _____________________________________________________________________________ -void ShapeBuilder::buildGraph() { - osm::OsmBuilder osmBuilder; - - 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); - - for (auto& feedStop : *getFeedStops()) { - if (feedStop.second) { - feedStop.second->pl().getSI()->getGroup()->writePens( - _motCfg.osmBuildOpts.trackNormzer, - _motCfg.routingOpts.platformUnmatchedPen, - _motCfg.routingOpts.stationDistPenFactor, - _motCfg.routingOpts.nonOsmPen); - } - } -} - // _____________________________________________________________________________ NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const { router::NodeCandRoute ncr(trip->getStopTimes().size()); @@ -613,14 +567,14 @@ bool ShapeBuilder::routingEqual(Trip* a, Trip* b) { } // _____________________________________________________________________________ -const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return &_g; } +const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return _g; } // _____________________________________________________________________________ void ShapeBuilder::writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, const Cluster& cluster) const { for (auto hop : shp.hops) { for (const auto* e : hop.edges) { - if (e->pl().isRev()) e = _g.getEdg(e->getTo(), e->getFrom()); + if (e->pl().isRev()) e = _g->getEdg(e->getTo(), e->getFrom()); (*edgs)[e].insert(cluster.begin(), cluster.end()); } } diff --git a/src/pfaedle/router/ShapeBuilder.h b/src/pfaedle/router/ShapeBuilder.h index 3a88b93..560d1b2 100644 --- a/src/pfaedle/router/ShapeBuilder.h +++ b/src/pfaedle/router/ShapeBuilder.h @@ -51,7 +51,8 @@ class ShapeBuilder { public: ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots, const config::MotConfig& motCfg, eval::Collector* ecoll, - const config::Config& cfg); + trgraph::Graph* g, router::FeedStops* stops, + osm::Restrictor* restr, const config::Config& cfg); void shape(pfaedle::netgraph::Graph* ng); @@ -79,10 +80,10 @@ class ShapeBuilder { config::MotConfig _motCfg; eval::Collector* _ecoll; config::Config _cfg; - trgraph::Graph _g; + trgraph::Graph* _g; router::Router _crouter; - router::FeedStops _stops; + router::FeedStops* _stops; NodeCandGroup _emptyNCG; @@ -92,10 +93,9 @@ class ShapeBuilder { TripRAttrs _rAttrs; - osm::Restrictor _restr; + osm::Restrictor* _restr; - void writeMotStops(); - void buildGraph(); + void buildGraph(router::FeedStops* fStops); Clusters clusterTrips(Feed* f, MOTs mots); void writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, diff --git a/src/pfaedle/trgraph/NodePL.cpp b/src/pfaedle/trgraph/NodePL.cpp index ad5f5cd..a0aec0a 100644 --- a/src/pfaedle/trgraph/NodePL.cpp +++ b/src/pfaedle/trgraph/NodePL.cpp @@ -129,6 +129,12 @@ util::json::Dict NodePL::getAttrs() const { obj["station_group"] = std::to_string(reinterpret_cast(_si->getGroup())); +#ifdef PFAEDLE_STATION_IDS + // only print this in debug mode + obj["station_id"] = _si->getId(); +#endif + + std::stringstream gtfsIds; if (_si->getGroup()) { for (auto* s : _si->getGroup()->getStops()) { diff --git a/src/pfaedle/trgraph/StatInfo.cpp b/src/pfaedle/trgraph/StatInfo.cpp index 1893244..0b642b6 100644 --- a/src/pfaedle/trgraph/StatInfo.cpp +++ b/src/pfaedle/trgraph/StatInfo.cpp @@ -22,6 +22,9 @@ StatInfo::StatInfo(const StatInfo& si) _fromOsm(si._fromOsm), _group(0) { setGroup(si._group); +#ifdef PFAEDLE_STATION_IDS + _id = si._id; +#endif } // _____________________________________________________________________________ diff --git a/src/pfaedle/trgraph/StatInfo.h b/src/pfaedle/trgraph/StatInfo.h index eedc378..de0bbcf 100644 --- a/src/pfaedle/trgraph/StatInfo.h +++ b/src/pfaedle/trgraph/StatInfo.h @@ -55,6 +55,11 @@ class StatInfo { // Set this stop as coming from osm void setIsFromOsm(bool is); +#ifdef PFAEDLE_STATION_IDS + const std::string& getId() const { return _id; } + void setId(const std::string& id) { _id = id; } +#endif + private: std::string _name; std::vector _altNames; @@ -62,6 +67,12 @@ class StatInfo { bool _fromOsm; StatGroup* _group; +#ifdef PFAEDLE_STATION_IDS + // debug feature to store station ids from both OSM + // and GTFS + std::string _id; +#endif + static std::unordered_map _groups; static void unRefGroup(StatGroup* g); }; diff --git a/src/util/Misc.h b/src/util/Misc.h index 75cb6f9..17b267e 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -7,6 +7,7 @@ #include #include +#include #define UNUSED(expr) do { (void)(expr); } while (0) #define TIME() std::chrono::high_resolution_clock::now() @@ -39,10 +40,18 @@ inline uint64_t atoul(const char* p) { } // _____________________________________________________________________________ -inline float atof(const char* p, uint8_t mn) { +inline bool isFloatingPoint(const std::string& str) { + std::stringstream ss(str); + double f; + ss >> std::noskipws >> f; + return ss.eof() && ! ss.fail(); +} + +// _____________________________________________________________________________ +inline double atof(const char* p, uint8_t mn) { // this atof implementation works only on "normal" float strings like // 56.445 or -345.00, but should be faster than std::atof - float ret = 0.0; + double ret = 0.0; bool neg = false; if (*p == '-') { neg = true; @@ -56,14 +65,14 @@ inline float atof(const char* p, uint8_t mn) { if (*p == '.') { p++; - float f = 0; + double f = 0; uint8_t n = 0; for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) { f = f * 10.0 + (*p - '0'); } - if (n < 11) + if (n < 10) ret += f / pow10[n]; else ret += f / std::pow(10, n); diff --git a/src/util/String.h b/src/util/String.h index fdb2ef3..b2dad5e 100644 --- a/src/util/String.h +++ b/src/util/String.h @@ -175,6 +175,18 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s) { return prefixEditDist(prefix, s, s.size()); } +// _____________________________________________________________________________ +inline std::string toUpper(std::string str) { + std::transform(str.begin(), str.end(),str.begin(), toupper); + return str; +} + +// _____________________________________________________________________________ +inline std::string toLower(std::string str) { + std::transform(str.begin(), str.end(),str.begin(), tolower); + return str; +} + // _____________________________________________________________________________ template inline std::string implode(Iter begin, const Iter& end, const char* del) { @@ -190,6 +202,25 @@ inline std::string implode(Iter begin, const Iter& end, const char* del) { return ss.str(); } +// _____________________________________________________________________________ +inline std::string normalizeWhiteSpace(const std::string& input) { + std::string ret; + bool ws = false; + for (size_t i = 0; i < input.size(); i++) { + if (std::isspace(input[i])) { + if (!ws) { + ret += " "; + ws = true; + } + continue; + } else { + ws = false; + ret += input[i]; + } + } + return ret; +} + // _____________________________________________________________________________ template inline std::string implode(const std::vector& vec, const char* del) { diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index 5cfa6cf..c4e3c5d 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -13,6 +13,7 @@ #include #include #include "util/Misc.h" +#include "util/String.h" #include "util/geo/Box.h" #include "util/geo/Line.h" #include "util/geo/Point.h" @@ -734,6 +735,45 @@ inline double dist(const Point& p1, const Point& p2) { return dist(p1.getX(), p1.getY(), p2.getX(), p2.getY()); } +// _____________________________________________________________________________ +template +inline Point pointFromWKT(std::string wkt) { + wkt = util::normalizeWhiteSpace(util::trim(wkt)); + if (wkt.rfind("POINT") == 0 || wkt.rfind("MPOINT") == 0) { + size_t b = wkt.find("(") + 1; + size_t e = wkt.find(")", b); + if (b > e) throw std::runtime_error("Could not parse WKT"); + auto xy = util::split(util::trim(wkt.substr(b, e - b)), ' '); + if (xy.size() < 2) throw std::runtime_error("Could not parse WKT"); + double x = atof(xy[0].c_str()); + double y = atof(xy[1].c_str()); + return Point(x, y); + } + throw std::runtime_error("Could not parse WKT"); +} + +// _____________________________________________________________________________ +template +inline Line lineFromWKT(std::string wkt) { + wkt = util::normalizeWhiteSpace(util::trim(wkt)); + if (wkt.rfind("LINESTRING") == 0 || wkt.rfind("MLINESTRING") == 0) { + Line ret; + size_t b = wkt.find("(") + 1; + size_t e = wkt.find(")", b); + if (b > e) throw std::runtime_error("Could not parse WKT"); + auto pairs = util::split(wkt.substr(b, e - b), ','); + for (const auto& p : pairs) { + auto xy = util::split(util::trim(p), ' '); + if (xy.size() < 2) throw std::runtime_error("Could not parse WKT"); + double x = atof(xy[0].c_str()); + double y = atof(xy[1].c_str()); + ret.push_back({x, y}); + } + return ret; + } + throw std::runtime_error("Could not parse WKT"); +} + // _____________________________________________________________________________ template inline std::string getWKT(const Point& p) { @@ -1479,6 +1519,24 @@ inline double webMercMeterDist(const G1& a, const G2& b) { return util::geo::dist(a, b) * cos((latA + latB) / 2.0); } + +// _____________________________________________________________________________ +template +inline double webMercLen(const Line& g) { + double ret = 0; + for (size_t i = 1; i < g.size(); i++) ret += webMercMeterDist(g[i - 1], g[i]); + return ret; +} + +// _____________________________________________________________________________ +template +inline double webMercDistFactor(const G& a) { + // euclidean distance on web mercator is in meters on equator, + // and proportional to cos(lat) in both y directions + + double lat = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965; + return cos(lat); +} } } diff --git a/src/util/geo/output/GeoGraphJsonOutput.h b/src/util/geo/output/GeoGraphJsonOutput.h index e8f9557..dcfb98b 100644 --- a/src/util/geo/output/GeoGraphJsonOutput.h +++ b/src/util/geo/output/GeoGraphJsonOutput.h @@ -11,9 +11,6 @@ #include "util/geo/output/GeoJsonOutput.h" #include "util/graph/Graph.h" -using util::toString; -using util::graph::Graph; - namespace util { namespace geo { namespace output { @@ -22,7 +19,7 @@ class GeoGraphJsonOutput { public: inline GeoGraphJsonOutput(){}; template - void print(const Graph& outG, std::ostream& str); + void print(const util::graph::Graph& outG, std::ostream& str); private: template diff --git a/src/util/geo/output/GeoGraphJsonOutput.tpp b/src/util/geo/output/GeoGraphJsonOutput.tpp index 7c2517e..c6341cc 100644 --- a/src/util/geo/output/GeoGraphJsonOutput.tpp +++ b/src/util/geo/output/GeoGraphJsonOutput.tpp @@ -22,10 +22,10 @@ void GeoGraphJsonOutput::print(const util::graph::Graph& outG, for (util::graph::Node* n : outG.getNds()) { if (!n->pl().getGeom()) continue; - json::Dict props{{"id", toString(n)}, - {"deg", toString(n->getDeg())}, - {"deg_out", toString(n->getOutDeg())}, - {"deg_in", toString(n->getInDeg())}}; + json::Dict props{{"id", util::toString(n)}, + {"deg", util::toString(n->getDeg())}, + {"deg_out", util::toString(n->getOutDeg())}, + {"deg_in", util::toString(n->getInDeg())}}; auto addProps = n->pl().getAttrs(); props.insert(addProps.begin(), addProps.end()); @@ -38,9 +38,9 @@ void GeoGraphJsonOutput::print(const util::graph::Graph& outG, for (graph::Edge* e : n->getAdjListOut()) { // to avoid double output for undirected graphs if (e->getFrom() != n) continue; - json::Dict props{{"from", toString(e->getFrom())}, - {"to", toString(e->getTo())}, - {"id", toString(e)}}; + json::Dict props{{"from", util::toString(e->getFrom())}, + {"to", util::toString(e->getTo())}, + {"id", util::toString(e)}}; auto addProps = e->pl().getAttrs(); props.insert(addProps.begin(), addProps.end()); diff --git a/src/util/geo/output/GeoJsonOutput.cpp b/src/util/geo/output/GeoJsonOutput.cpp index 002c37a..6b638da 100644 --- a/src/util/geo/output/GeoJsonOutput.cpp +++ b/src/util/geo/output/GeoJsonOutput.cpp @@ -17,9 +17,18 @@ GeoJsonOutput::GeoJsonOutput(std::ostream& str) : _wr(&str, 10, true) { } // _____________________________________________________________________________ -GeoJsonOutput::~GeoJsonOutput() { - flush(); +GeoJsonOutput::GeoJsonOutput(std::ostream& str, json::Val attrs) + : _wr(&str, 10, true) { + _wr.obj(); + _wr.keyVal("type", "FeatureCollection"); + _wr.key("properties"); + _wr.val(attrs); + _wr.key("features"); + _wr.arr(); } +// _____________________________________________________________________________ +GeoJsonOutput::~GeoJsonOutput() { flush(); } + // _____________________________________________________________________________ void GeoJsonOutput::flush() { _wr.closeAll(); } diff --git a/src/util/geo/output/GeoJsonOutput.h b/src/util/geo/output/GeoJsonOutput.h index 834507c..f4a8492 100644 --- a/src/util/geo/output/GeoJsonOutput.h +++ b/src/util/geo/output/GeoJsonOutput.h @@ -19,6 +19,7 @@ namespace output { class GeoJsonOutput { public: GeoJsonOutput(std::ostream& str); + GeoJsonOutput(std::ostream& str, json::Val attrs); ~GeoJsonOutput(); template void print(const Point& p, json::Val attrs); diff --git a/src/util/tests/TestMain.cpp b/src/util/tests/TestMain.cpp index bc36557..d7590d3 100644 --- a/src/util/tests/TestMain.cpp +++ b/src/util/tests/TestMain.cpp @@ -1001,9 +1001,82 @@ CASE("nullable") { } }}, +// ___________________________________________________________________________ +{ +CASE("geomwkt") { + auto p = pointFromWKT("POINT(10 50)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT( 10 50)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT (10 50 30)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT (10 50 30)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT(10 50 30)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT (10 50) "); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("MPOINT(10 50 30)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("MPOINT(10 50)"); + EXPECT(p.getX() == approx(10)); + EXPECT(p.getY() == approx(50)); + + p = pointFromWKT("POINT(10.05 50.05)"); + EXPECT(p.getX() == approx(10.05)); + EXPECT(p.getY() == approx(50.05)); + + auto wktl = lineFromWKT("LINESTRING(0 0, 1 1,2 3, 0 1)"); + EXPECT(wktl.size() == (size_t)4); + EXPECT(wktl[0].getX() == approx(0)); + EXPECT(wktl[0].getY() == approx(0)); + EXPECT(wktl[1].getX() == approx(1)); + EXPECT(wktl[1].getY() == approx(1)); + EXPECT(wktl[2].getX() == approx(2)); + EXPECT(wktl[2].getY() == approx(3)); + EXPECT(wktl[3].getX() == approx(0)); + EXPECT(wktl[3].getY() == approx(1)); + + wktl = lineFromWKT("MLINESTRING(0 0, 1 1,2 3, 0 1)"); + EXPECT(wktl.size() == (size_t)4); + EXPECT(wktl[0].getX() == approx(0)); + EXPECT(wktl[0].getY() == approx(0)); + EXPECT(wktl[1].getX() == approx(1)); + EXPECT(wktl[1].getY() == approx(1)); + EXPECT(wktl[2].getX() == approx(2)); + EXPECT(wktl[2].getY() == approx(3)); + EXPECT(wktl[3].getX() == approx(0)); + EXPECT(wktl[3].getY() == approx(1)); + + wktl = lineFromWKT("MLINESTRING (0 0, 1 1,2 3, 0 1 )"); + EXPECT(wktl.size() == (size_t)4); + EXPECT(wktl[0].getX() == approx(0)); + EXPECT(wktl[0].getY() == approx(0)); + EXPECT(wktl[1].getX() == approx(1)); + EXPECT(wktl[1].getY() == approx(1)); + EXPECT(wktl[2].getX() == approx(2)); + EXPECT(wktl[2].getY() == approx(3)); + EXPECT(wktl[3].getX() == approx(0)); + EXPECT(wktl[3].getY() == approx(1)); +}}, // ___________________________________________________________________________ { CASE("geometry") { + geo::Point a(1, 2); geo::Point b(2, 3); geo::Point c(4, 5); From 89be7a17600c6e65f1cc40566d64ec066aae1c94 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sun, 3 Feb 2019 14:14:31 +0100 Subject: [PATCH 005/182] handle station entrances (should only occur in erroneous feeds) --- src/pfaedle/router/Misc.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index 4380128..c477b09 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -11,8 +11,8 @@ #include #include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Route.h" -#include "pfaedle/trgraph/Graph.h" #include "pfaedle/gtfs/Feed.h" +#include "pfaedle/trgraph/Graph.h" #include "util/Nullable.h" using ad::cppgtfs::gtfs::Route; @@ -166,7 +166,18 @@ inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed, for (auto t : feed->getTrips()) { if (!tid.empty() && t.getId() != tid) continue; if (mots.count(t.getRoute()->getType())) { - for (auto st : t.getStopTimes()) ret[st.getStop()] = 0; + for (auto st : t.getStopTimes()) { + // if the station has type STATION_ENTRANCE, use the parent + // station for routing. Normally, this should not occur, as + // this is not allowed in stop_times.txt + if (st.getStop()->getLocationType() == + ad::cppgtfs::gtfs::flat::Stop::STATION_ENTRANCE && + st.getStop()->getParentStation()) { + ret[st.getStop()->getParentStation()] = 0; + } else { + ret[st.getStop()] = 0; + } + } } } return ret; From 549cc90616062d476e30496894e30cf5872c8d31 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sun, 3 Feb 2019 19:33:35 +0100 Subject: [PATCH 006/182] use current dir as default dbg path, not ./geo --- src/CMakeLists.txt | 4 +++- src/pfaedle/config/ConfigReader.cpp | 2 +- src/pfaedle/config/PfaedleConfig.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14edcfc..4bb21a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,8 @@ set(PFAEDLE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -cpplint_add_subdirectory(pfaedle) +if (COMMAND cpplint_add_subdirectory) + cpplint_add_subdirectory(pfaedle) +endif() include_directories( ${PFAEDLE_INCLUDE_DIR} diff --git a/src/pfaedle/config/ConfigReader.cpp b/src/pfaedle/config/ConfigReader.cpp index d38c347..c9ef614 100644 --- a/src/pfaedle/config/ConfigReader.cpp +++ b/src/pfaedle/config/ConfigReader.cpp @@ -73,7 +73,7 @@ void ConfigReader::help(const char* bin) { << std::setw(35) << " --inplace" << "overwrite input GTFS feed with output feed\n" << "\nDebug Output:\n" - << std::setw(35) << " -d [ --dbg-path ] arg (=geo)" + << std::setw(35) << " -d [ --dbg-path ] arg (=.)" << "output path for debug files\n" << std::setw(35) << " --write-trgraph" << "write transit graph as GeoJSON to\n" diff --git a/src/pfaedle/config/PfaedleConfig.h b/src/pfaedle/config/PfaedleConfig.h index f209c0c..e5bc41a 100644 --- a/src/pfaedle/config/PfaedleConfig.h +++ b/src/pfaedle/config/PfaedleConfig.h @@ -18,7 +18,7 @@ using ad::cppgtfs::gtfs::Route; struct Config { Config() - : dbgOutputPath("geo"), + : dbgOutputPath("."), solveMethod("global"), evalPath("."), outputPath("gtfs-out"), From 02bda75f016bdb28683748ad63b784b666139140 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 4 Feb 2019 02:49:41 +0100 Subject: [PATCH 007/182] update submodules --- src/configparser | 2 +- src/cppgtfs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configparser b/src/configparser index afacc8b..54da092 160000 --- a/src/configparser +++ b/src/configparser @@ -1 +1 @@ -Subproject commit afacc8bc778bd1e38ae1c4466d1eb10ce92ba96e +Subproject commit 54da0925a2a15ddaed209f67a6aa5d7a4cd65426 diff --git a/src/cppgtfs b/src/cppgtfs index 212fdc7..8c256e6 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 212fdc7c8aff50b0f7a872ccb40850f8080eb402 +Subproject commit 8c256e6e1ae8ed61639700e90802ccbefbcd4c34 From c978217114ceaacc2915b93c52e7ee0b147db50d Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 4 Feb 2019 16:43:32 +0100 Subject: [PATCH 008/182] fix some typos in the readme --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c950989..0d499ed 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://tr # pfaedle -Precise map-matching for public transit schedules (GTFS data). +Precise map-matching for public transit schedules [GTFS](https://developers.google.com/transit/gtfs/reference/) data. +Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf). ## Requirements @@ -58,7 +59,7 @@ $ pfaedle -D -x freiburg-regbez-latest.osm . ## Generating shapes for a specific MOT -To generate shapes only for a specific mot, use the `-m` option. Possible +To generate shapes for a specific mot only, use the `-m` option. Possible values are either `tram`, `bus`, `coach`, `rail`, `subway`, `ferry`, `funicular`, `gondola`, `all` (default) or GTFS vehicle type codes (0, 1, 2, 3, 4, 5, 6, 7). @@ -71,7 +72,7 @@ the `-X` flag, `pfaedle` will filter the input OSM file and output a new OSM file which contains exactly the data needed to calculate the shapes for the input GTFS feed and the input configuration. -This can be used to avoid parsing (for example) the entire world.osm on each +This can be used to avoid parsing (for example) the entire `planet.osm` on each run. ## Debugging @@ -103,7 +104,7 @@ make eval ``` *Notes:* - * this will download, and filter, the entire OSM files for Spain, the + * this will download, and filter, the entire OSM files for Spain and the Stuttgart region. Make sure you have enough space left on your hard drive. * in evaluation mode, pfaedle needs significantly more time, because the calculation of the similarity measurements between shapes are expensive From e00e536bf27b080f2d5cfb922794c827ff08e15a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 4 Feb 2019 16:46:14 +0100 Subject: [PATCH 009/182] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d499ed..4294cb9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://tr # pfaedle -Precise map-matching for public transit schedules [GTFS](https://developers.google.com/transit/gtfs/reference/) data. +Precise map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data). Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf). ## Requirements From ff7f8105918d9a4cf92142c46d2ee40c517b712c Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 4 Feb 2019 16:46:56 +0100 Subject: [PATCH 010/182] be more precise in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4294cb9..c21c924 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://tr # pfaedle -Precise map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data). +Precise OpenStreetMap (OSM) map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data). Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf). ## Requirements From 3e8672a268b78336140d62d981b929d1970aa91b Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Wed, 6 Feb 2019 01:51:24 +0100 Subject: [PATCH 011/182] fix typo in heur dist --- src/pfaedle/router/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp index 4835653..38e49c7 100644 --- a/src/pfaedle/router/Router.cpp +++ b/src/pfaedle/router/Router.cpp @@ -170,7 +170,7 @@ DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, EdgeCost DistHeur::operator()(const trgraph::Edge* a, const std::set& b) const { UNUSED(b); - double cur = webMercMeterDist(*a->getTo()->pl().getGeom(), _center) * + double cur = webMercMeterDist(*a->getFrom()->pl().getGeom(), _center) * _rOpts.levelPunish[_lvl]; return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); From d995f77b53493dc4413b735a1469e0e9b1b4ed1b Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 11 Feb 2019 12:37:07 +0100 Subject: [PATCH 012/182] add missing stdexcept includes --- CMakeLists.txt | 6 +++--- src/pfaedle/router/ShapeBuilder.cpp | 1 + src/pfaedle/trgraph/Normalizer.cpp | 1 + src/util/geo/Geo.h | 1 + src/util/http/Server.cpp | 8 ++++---- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 190e2ba..3163cf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,14 +75,14 @@ add_custom_target( eval COMMAND make WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval - ) +) # handles install target install( FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS WORLD_READ - ) +) install( FILES build/pfaedle DESTINATION bin PERMISSIONS WORLD_EXECUTE COMPONENT binaries - ) +) diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index 67073b8..ffc56f2 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "ad/cppgtfs/gtfs/Feed.h" diff --git a/src/pfaedle/trgraph/Normalizer.cpp b/src/pfaedle/trgraph/Normalizer.cpp index 9db38d0..173444e 100644 --- a/src/pfaedle/trgraph/Normalizer.cpp +++ b/src/pfaedle/trgraph/Normalizer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index c4e3c5d..679d9cd 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "util/Misc.h" #include "util/String.h" #include "util/geo/Box.h" diff --git a/src/util/http/Server.cpp b/src/util/http/Server.cpp index 87957c2..dacee83 100644 --- a/src/util/http/Server.cpp +++ b/src/util/http/Server.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #ifdef ZLIB_FOUND @@ -108,8 +109,7 @@ void HttpServer::handle() { answ = Answer(err.what(), err.what()); } catch (...) { // catch everything to make sure the server continues running - answ = Answer( - "500 Internal Server Error", "500 Internal Server Error"); + answ = Answer("500 Internal Server Error", "500 Internal Server Error"); } send(connection, &answ); @@ -146,8 +146,8 @@ Req HttpServer::getReq(int connection) { int64_t curRcvd = 0; HeaderState state = NONE; Req ret; - char *tmp = 0; - char *tmp2 = 0; + char* tmp = 0; + char* tmp2 = 0; char* brk = 0; while ((curRcvd = read(connection, buf + rcvd, BSIZE - rcvd))) { From 1dff5eaf643d6fda3d04345d6a385394e11e7d3c Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 11 Feb 2019 17:55:05 +0100 Subject: [PATCH 013/182] catch polymorphic exceptions by const reference --- src/pfaedle/PfaedleMain.cpp | 12 ++++++------ src/util/http/Server.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 0f93134..80a8ec4 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -80,7 +80,7 @@ int main(int argc, char** argv) { try { motCfgReader.parse(cfgPaths); - } catch (configparser::ParseExc ex) { + } catch (const configparser::ParseExc& ex) { LOG(ERROR) << "Could not parse MOT configurations, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::MOT_CFG_PARSE_ERR)); @@ -108,7 +108,7 @@ int main(int argc, char** argv) { // read the shapes and store them in memory p.parseShapes(&evalFeed, cfg.feedPaths[0]); } - } catch (ad::cppgtfs::ParserException ex) { + } catch (const ad::cppgtfs::ParserException& ex) { LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_PARSE_ERR)); @@ -121,7 +121,7 @@ int main(int argc, char** argv) { ad::cppgtfs::Parser p; try { p.parse(>fs[i], cfg.feedPaths[i]); - } catch (ad::cppgtfs::ParserException ex) { + } catch (const ad::cppgtfs::ParserException& ex) { LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_PARSE_ERR)); @@ -167,7 +167,7 @@ int main(int argc, char** argv) { } try { osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box); - } catch (xml::XmlFileException ex) { + } catch (const xml::XmlFileException& ex) { LOG(ERROR) << "Could not parse OSM data, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::OSM_PARSE_ERR)); @@ -276,7 +276,7 @@ int main(int argc, char** argv) { out.print(ng, fstr); fstr.close(); } - } catch (xml::XmlFileException ex) { + } catch (const xml::XmlFileException& ex) { LOG(ERROR) << "Could not parse OSM data, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::OSM_PARSE_ERR)); @@ -291,7 +291,7 @@ int main(int argc, char** argv) { LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ..."; pfaedle::gtfs::Writer w; w.write(>fs[0], cfg.outputPath); - } catch (ad::cppgtfs::WriterException ex) { + } catch (const ad::cppgtfs::WriterException& ex) { LOG(ERROR) << "Could not write final GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_WRITE_ERR)); diff --git a/src/util/http/Server.cpp b/src/util/http/Server.cpp index dacee83..fa20a97 100644 --- a/src/util/http/Server.cpp +++ b/src/util/http/Server.cpp @@ -105,7 +105,7 @@ void HttpServer::handle() { Req req = getReq(connection); answ = _handler->handle(req, connection); answ.gzip = gzipSupport(req); - } catch (HttpErr err) { + } catch (const HttpErr& err) { answ = Answer(err.what(), err.what()); } catch (...) { // catch everything to make sure the server continues running From f6f1de05d2777dabfad37cc8fbe8cd6bb381b0e8 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 11 Feb 2019 17:55:38 +0100 Subject: [PATCH 014/182] remove unused class member --- src/util/http/Server.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/http/Server.h b/src/util/http/Server.h index ccefed7..03cb279 100644 --- a/src/util/http/Server.h +++ b/src/util/http/Server.h @@ -48,7 +48,6 @@ class HttpErr : public std::exception { private: std::string _msg; - uint16_t _code; }; /* From a2e5b3be3263499d624b9dfdef32e9f078f6d72a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 1 Apr 2019 21:56:52 +0200 Subject: [PATCH 015/182] only cpplint in debug builds to avoid python2 dependency --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3163cf4..5016463 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,13 @@ cmake_minimum_required (VERSION 2.8) project (pfaedle) -set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py") -include(cmake/cpplint.cmake) +if (CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE) +endif() +if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") + set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py") + include(cmake/cpplint.cmake) +endif() set(CPPLINT_PROJECT_ROOT "src") From 9d1123e86e1d4a10b87b4b8fed85f975be8f6e0d Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Mon, 8 Apr 2019 12:38:38 +0200 Subject: [PATCH 016/182] Explicitly use python 2 in shebang, fixes #3 --- cpplint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpplint.py b/cpplint.py index db1c123..294337d 100755 --- a/cpplint.py +++ b/cpplint.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright (c) 2009 Google Inc. All rights reserved. # From 78f9eb70445c6d2559ae5255feed1b4a22b27b69 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Wed, 1 May 2019 22:47:00 +0200 Subject: [PATCH 017/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index 8c256e6..f5391e1 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 8c256e6e1ae8ed61639700e90802ccbefbcd4c34 +Subproject commit f5391e1567f4f21379f7b1fb6d82fc904a61c434 From 0db43ad457645cfcbb045a411e0495cbe63c6349 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 2 May 2019 01:20:35 +0200 Subject: [PATCH 018/182] update cfgparser --- src/configparser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configparser b/src/configparser index 54da092..b2d0c99 160000 --- a/src/configparser +++ b/src/configparser @@ -1 +1 @@ -Subproject commit 54da0925a2a15ddaed209f67a6aa5d7a4cd65426 +Subproject commit b2d0c99b9c84f62f5f7b259524e0f4b1c9d38318 From ee948a88108fcafe5fc45fff0a38a5f591b1eb17 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 13 May 2019 15:05:34 +0200 Subject: [PATCH 019/182] prevent possible race condition while obtaining collision-free shape ID --- src/pfaedle/router/ShapeBuilder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index ffc56f2..b96f8a6 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -242,6 +242,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { for (auto t : clusters[i]) { if (_cfg.evaluate && _evalFeed && _ecoll) { + std::lock_guard guard(_shpMutex); _ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp, distances); } @@ -249,6 +250,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { if (!t->getShape().empty() && shpUsage[t->getShape()] > 0) { shpUsage[t->getShape()]--; if (shpUsage[t->getShape()] == 0) { + std::lock_guard guard(_shpMutex); _feed->getShapes().remove(t->getShape()); } } @@ -289,7 +291,6 @@ void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, } std::lock_guard guard(_shpMutex); - // TODO(patrick): t->setShape(_feed->getShapes().add(s)); } From 67308d02e93b8075d9f67fc43f2dd2f8ca4618bf Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 13 May 2019 18:56:43 +0200 Subject: [PATCH 020/182] centralize code for tmp storage file creation --- src/pfaedle/Def.h | 30 +++++++++++ src/pfaedle/gtfs/ShapeContainer.h | 3 ++ src/pfaedle/gtfs/ShapeContainer.tpp | 16 ++---- src/pfaedle/gtfs/Writer.cpp | 81 +++++++++++++++-------------- src/pfaedle/gtfs/Writer.h | 2 +- src/pfaedle/osm/OsmIdSet.cpp | 28 +++------- src/pfaedle/osm/OsmIdSet.h | 3 +- 7 files changed, 87 insertions(+), 76 deletions(-) diff --git a/src/pfaedle/Def.h b/src/pfaedle/Def.h index 02e8002..cde8959 100644 --- a/src/pfaedle/Def.h +++ b/src/pfaedle/Def.h @@ -5,6 +5,8 @@ #ifndef PFAEDLE_DEF_H_ #define PFAEDLE_DEF_H_ +#include +#include "util/log/Log.h" #include "util/geo/Geo.h" #include "util/geo/PolyLine.h" @@ -27,4 +29,32 @@ #define BOX_PADDING 2500 +namespace pfaedle { + +// _____________________________________________________________________________ +inline std::string getTmpFName(std::string dir, std::string postf) { + if (postf.size()) postf = "-" + postf; + if (dir.size() && dir.back() != '/') dir = dir + "/"; + + std::string f = dir + ".pfaedle-tmp" + postf; + + size_t c = 0; + + while (access(f.c_str(), F_OK) != -1) { + c++; + if (c > 10000) { + // giving up... + LOG(ERROR) << "Could not find temporary file name!"; + exit(1); + } + std::stringstream ss; + ss << dir << ".pfaedle-tmp" << postf << "-" << std::rand(); + f = ss.str().c_str(); + } + + return f; +} + +} + #endif // PFAEDLE_DEF_H_ diff --git a/src/pfaedle/gtfs/ShapeContainer.h b/src/pfaedle/gtfs/ShapeContainer.h index 6a675b8..0e25cb1 100644 --- a/src/pfaedle/gtfs/ShapeContainer.h +++ b/src/pfaedle/gtfs/ShapeContainer.h @@ -5,13 +5,16 @@ #ifndef PFAEDLE_GTFS_SHAPECONTAINER_H_ #define PFAEDLE_GTFS_SHAPECONTAINER_H_ +#include #include #include #include #include #include +#include #include "ad/cppgtfs/gtfs/Shape.h" #include "ad/cppgtfs/gtfs/flat/Shape.h" +#include "pfaedle/Def.h" #include "util/Misc.h" namespace pfaedle { diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index 081a47b..e4e5395 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -2,28 +2,18 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi -#include -#include - // ____________________________________________________________________________ template ShapeContainer::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(); - } - + std::string f = pfaedle::getTmpFName("", ""); _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; + std::cerr << "Could not open temporary file " << f + << std::endl; exit(1); } } diff --git a/src/pfaedle/gtfs/Writer.cpp b/src/pfaedle/gtfs/Writer.cpp index 4042da6..7a0c5b3 100644 --- a/src/pfaedle/gtfs/Writer.cpp +++ b/src/pfaedle/gtfs/Writer.cpp @@ -16,6 +16,7 @@ using ad::util::CsvWriter; using ad::cppgtfs::Parser; using pfaedle::gtfs::Writer; +using pfaedle::getTmpFName; // ____________________________________________________________________________ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { @@ -25,138 +26,145 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { std::string curFile; std::string curFileTg; - curFile = getTmpFName("agency.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); - curFile = getTmpFName("stops.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); - curFile = getTmpFName("routes.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); is.open((sourceFeed->getPath() + "/calendar.txt").c_str()); if (is.good()) { is.close(); - curFile = getTmpFName("calendar.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str()); if (is.good()) { is.close(); - curFile = getTmpFName("calendar_dates.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/transfers.txt").c_str()); if (is.good()) { is.close(); - curFile = getTmpFName("transfers.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str()); if (is.good()) { is.close(); - curFile = getTmpFName("fare_attributes.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str()); if (is.good()) { is.close(); - curFile = getTmpFName("fare_rules.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.close(); - curFile = getTmpFName("shapes.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); is.close(); - curFile = getTmpFName("trips.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); is.open((sourceFeed->getPath() + "/frequencies.txt").c_str()); if (hasFreqs && is.good()) { is.close(); - curFile = getTmpFName("frequencies.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } is.close(); - curFile = getTmpFName("stop_times.txt"); + curFile = getTmpFName(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 (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); if (!sourceFeed->getPublisherUrl().empty() && !sourceFeed->getPublisherName().empty()) { - curFile = getTmpFName("feed_info.txt"); + curFile = getTmpFName(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()); + if (std::rename(curFile.c_str(), curFileTg.c_str())) + cannotWrite(curFileTg); } return true; @@ -487,6 +495,13 @@ bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { return true; } +// ___________________________________________________________________________ +void Writer::cannotWrite(const std::string& file) { + std::stringstream ss; + ss << "Could not write to file"; + throw ad::cppgtfs::WriterException(ss.str(), file); +} + // ___________________________________________________________________________ void Writer::cannotWrite(const std::string& file, const std::string& file2) { std::stringstream ss; @@ -494,17 +509,3 @@ void Writer::cannotWrite(const std::string& file, const std::string& file2) { throw ad::cppgtfs::WriterException(ss.str(), file); } -// _____________________________________________________________________________ -std::string Writer::getTmpFName(const std::string& postf) { - std::string f = ".pfaedle-tmp-" + postf; - - while (access(f.c_str(), F_OK) != -1) { - std::stringstream ss; - ss << ".pfaedle-tmp-"; - ss << postf << "-"; - ss << std::rand(); - f = ss.str().c_str(); - } - - return f; -} diff --git a/src/pfaedle/gtfs/Writer.h b/src/pfaedle/gtfs/Writer.h index deb25f8..917195d 100644 --- a/src/pfaedle/gtfs/Writer.h +++ b/src/pfaedle/gtfs/Writer.h @@ -34,7 +34,7 @@ class Writer { bool writeStopTimes(Feed* f, std::ostream* os) const; static void cannotWrite(const std::string& file, const std::string& file2); - static std::string getTmpFName(const std::string& postf); + static void cannotWrite(const std::string& file); }; } // namespace gtfs diff --git a/src/pfaedle/osm/OsmIdSet.cpp b/src/pfaedle/osm/OsmIdSet.cpp index 5390def..63a12fc 100644 --- a/src/pfaedle/osm/OsmIdSet.cpp +++ b/src/pfaedle/osm/OsmIdSet.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -13,6 +12,8 @@ #include #include #include +#include +#include "pfaedle/Def.h" #include "pfaedle/osm/OsmIdSet.h" using pfaedle::osm::OsmIdSet; @@ -48,7 +49,6 @@ OsmIdSet::~OsmIdSet() { void OsmIdSet::add(osmid id) { if (_closed) throw std::exception(); diskAdd(id); - // _set.insert(id); if (_last > id) _sorted = false; _last = id; @@ -134,7 +134,6 @@ bool OsmIdSet::has(osmid id) const { } bool has = diskHas(id); - // assert(has == (bool)_set.count(id)); return has; } @@ -232,7 +231,7 @@ void OsmIdSet::sort() const { size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const { ssize_t w = write(f, buf, n); if (w < 0) { - throw std::runtime_error("OSMIDSET: could not write to tmp file.\n"); + throw std::runtime_error("Could not write to tmp file.\n"); } return w; @@ -242,7 +241,7 @@ size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const { size_t OsmIdSet::cread(int f, void* buf, size_t n) const { ssize_t w = read(f, buf, n); if (w < 0) { - throw std::runtime_error("OSMIDSET: could not read from tmp file.\n"); + throw std::runtime_error("Could not read from tmp file.\n"); } return w; @@ -272,14 +271,15 @@ uint32_t OsmIdSet::hash(uint32_t in, int i) const { // _____________________________________________________________________________ int OsmIdSet::openTmpFile() const { - const std::string& fname = getFName(); + const std::string& fname = getTmpFName(_tmpPath, ""); int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666); // immediately unlink unlink(fname.c_str()); if (file < 0) { - std::cerr << "Could not open temporary file " << fname << std::endl; + std::cerr << "Could not open temporary file " << fname + << std::endl; exit(1); } @@ -288,17 +288,3 @@ int OsmIdSet::openTmpFile() const { #endif return file; } - -// _____________________________________________________________________________ -std::string OsmIdSet::getFName() const { - std::string f = ".pfaedle-tmp"; - - while (access(f.c_str(), F_OK) != -1) { - std::stringstream ss; - ss << ".pfaedle-tmp-"; - ss << std::rand(); - f = ss.str().c_str(); - } - - return f; -} diff --git a/src/pfaedle/osm/OsmIdSet.h b/src/pfaedle/osm/OsmIdSet.h index 24d356d..8eb703e 100644 --- a/src/pfaedle/osm/OsmIdSet.h +++ b/src/pfaedle/osm/OsmIdSet.h @@ -34,6 +34,7 @@ static const size_t OBUFFER_S = 8 * 1024 * 1024; class OsmIdSet { public: OsmIdSet(); + OsmIdSet(const std::string& tmpPath) : _tmpPath(tmpPath) {}; ~OsmIdSet(); // Add an OSM id @@ -47,7 +48,7 @@ class OsmIdSet { static size_t FLOOKUPS; private: - std::set _set; + std::string _tmpPath; mutable bool _closed; mutable int _file; unsigned char* _buffer; From 9d19e344d300e372f1b936521d405f0fbebf92a8 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 13 May 2019 02:27:12 +0200 Subject: [PATCH 021/182] refactoring --- src/pfaedle/PfaedleMain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 80a8ec4..2b37aea 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -334,6 +334,8 @@ std::vector getCfgPaths(const Config& cfg) { } } + if (buf) free(buf); + // install prefix global configuration path, if available { auto path = std::string(INSTALL_PREFIX) + @@ -361,8 +363,6 @@ std::vector getCfgPaths(const Config& cfg) { } } - if (buf) free(buf); - // CWD { char cwd[PATH_MAX]; From 347badf770666fe5e2df91727c119eaed3de9f25 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 14 May 2019 00:11:15 +0200 Subject: [PATCH 022/182] better tmp dir selection --- src/pfaedle/Def.h | 5 ++- src/pfaedle/PfaedleMain.cpp | 46 ++++++++++++++++------------ src/pfaedle/config/MotConfigReader.h | 29 ++---------------- src/pfaedle/gtfs/ShapeContainer.h | 1 - src/pfaedle/gtfs/ShapeContainer.tpp | 2 ++ src/pfaedle/osm/OsmIdSet.cpp | 5 ++- src/pfaedle/osm/OsmIdSet.h | 1 - src/util/Misc.h | 46 ++++++++++++++++++++++++++++ 8 files changed, 83 insertions(+), 52 deletions(-) diff --git a/src/pfaedle/Def.h b/src/pfaedle/Def.h index cde8959..7b8e083 100644 --- a/src/pfaedle/Def.h +++ b/src/pfaedle/Def.h @@ -6,7 +6,9 @@ #define PFAEDLE_DEF_H_ #include +#include #include "util/log/Log.h" +#include "util/Misc.h" #include "util/geo/Geo.h" #include "util/geo/PolyLine.h" @@ -34,6 +36,7 @@ namespace pfaedle { // _____________________________________________________________________________ inline std::string getTmpFName(std::string dir, std::string postf) { if (postf.size()) postf = "-" + postf; + if (!dir.size()) dir = util::getTmpDir(); if (dir.size() && dir.back() != '/') dir = dir + "/"; std::string f = dir + ".pfaedle-tmp" + postf; @@ -55,6 +58,6 @@ inline std::string getTmpFName(std::string dir, std::string postf) { return f; } -} +} // namespace pfaedle #endif // PFAEDLE_DEF_H_ diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 2b37aea..6a81f88 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -30,6 +30,29 @@ #include "util/geo/output/GeoJsonOutput.h" #include "util/json/Writer.h" #include "util/log/Log.h" +#include "util/Misc.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 using pfaedle::router::MOTs; using pfaedle::osm::BBoxIdx; @@ -316,25 +339,6 @@ std::vector getCfgPaths(const Config& cfg) { if (cfg.configPaths.size()) return cfg.configPaths; std::vector 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(-1)) bufsize = 0x4000; - buf = static_cast(malloc(bufsize)); - if (buf != 0) { - getpwuid_r(getuid(), &pwd, buf, bufsize, &result); - if (result != NULL) homedir = result->pw_dir; - } - } - - if (buf) free(buf); // install prefix global configuration path, if available { @@ -352,7 +356,7 @@ std::vector getCfgPaths(const Config& cfg) { // local user configuration path, if available { - auto path = std::string(homedir) + XDG_CONFIG_HOME_SUFFIX + "/" + + auto path = util::getHomeDir() + XDG_CONFIG_HOME_SUFFIX + "/" + "pfaedle" + "/" + CFG_FILE_NAME; std::ifstream is(path); @@ -363,6 +367,8 @@ std::vector getCfgPaths(const Config& cfg) { } } + // free this here, as we use homedir in the block above + // CWD { char cwd[PATH_MAX]; diff --git a/src/pfaedle/config/MotConfigReader.h b/src/pfaedle/config/MotConfigReader.h index 2e7fee2..51716ff 100644 --- a/src/pfaedle/config/MotConfigReader.h +++ b/src/pfaedle/config/MotConfigReader.h @@ -5,36 +5,13 @@ #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 -#include #include #include +#include +#include #include "ad/cppgtfs/gtfs/Route.h" #include "configparser/ConfigFileParser.h" +#include "pfaedle/_config.h" #include "pfaedle/config/MotConfig.h" #include "pfaedle/osm/OsmBuilder.h" diff --git a/src/pfaedle/gtfs/ShapeContainer.h b/src/pfaedle/gtfs/ShapeContainer.h index 0e25cb1..1128031 100644 --- a/src/pfaedle/gtfs/ShapeContainer.h +++ b/src/pfaedle/gtfs/ShapeContainer.h @@ -11,7 +11,6 @@ #include #include #include -#include #include "ad/cppgtfs/gtfs/Shape.h" #include "ad/cppgtfs/gtfs/flat/Shape.h" #include "pfaedle/Def.h" diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index e4e5395..b55d646 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -2,6 +2,8 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi +#include + // ____________________________________________________________________________ template ShapeContainer::ShapeContainer() { diff --git a/src/pfaedle/osm/OsmIdSet.cpp b/src/pfaedle/osm/OsmIdSet.cpp index 63a12fc..8b23bf9 100644 --- a/src/pfaedle/osm/OsmIdSet.cpp +++ b/src/pfaedle/osm/OsmIdSet.cpp @@ -271,15 +271,14 @@ uint32_t OsmIdSet::hash(uint32_t in, int i) const { // _____________________________________________________________________________ int OsmIdSet::openTmpFile() const { - const std::string& fname = getTmpFName(_tmpPath, ""); + const std::string& fname = getTmpFName("", ""); int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666); // immediately unlink unlink(fname.c_str()); if (file < 0) { - std::cerr << "Could not open temporary file " << fname - << std::endl; + std::cerr << "Could not open temporary file " << fname << std::endl; exit(1); } diff --git a/src/pfaedle/osm/OsmIdSet.h b/src/pfaedle/osm/OsmIdSet.h index 8eb703e..d19f9a1 100644 --- a/src/pfaedle/osm/OsmIdSet.h +++ b/src/pfaedle/osm/OsmIdSet.h @@ -34,7 +34,6 @@ static const size_t OBUFFER_S = 8 * 1024 * 1024; class OsmIdSet { public: OsmIdSet(); - OsmIdSet(const std::string& tmpPath) : _tmpPath(tmpPath) {}; ~OsmIdSet(); // Add an OSM id diff --git a/src/util/Misc.h b/src/util/Misc.h index 17b267e..6cd667d 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -6,8 +6,12 @@ #define UTIL_MISC_H_ #include +#include #include #include +#include +#include +#include #define UNUSED(expr) do { (void)(expr); } while (0) #define TIME() std::chrono::high_resolution_clock::now() @@ -85,6 +89,48 @@ inline double atof(const char* p, uint8_t mn) { // _____________________________________________________________________________ inline double atof(const char* p) { return atof(p, 38); } +// _____________________________________________________________________________ +inline std::string getHomeDir() { + // 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(-1)) bufsize = 0x4000; + buf = static_cast(malloc(bufsize)); + if (buf != 0) { + getpwuid_r(getuid(), &pwd, buf, bufsize, &result); + if (result != NULL) homedir = result->pw_dir; + } + } + + std::string ret(homedir); + if (buf) free(buf); + + return ret; +} + +// _____________________________________________________________________________ +inline std::string getTmpDir() { + // first, check if an env variable is set + const char* tmpdir = getenv("TMPDIR"); + if (std::strlen(tmpdir)) return std::string(tmpdir); + + // second, check if /tmp is writable + if (access("/tmp/", W_OK) == 0) return "/tmp"; + + // third, check if the cwd is writable + if (access(".", W_OK) == 0) return "."; + + // lastly, return the users home directory as a fallback + return getHomeDir(); +} + } // namespace util #endif // UTIL_MISC_H_ From df1ba83a53d363cbd98a135d62cd778f7f9955d0 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 14 May 2019 00:46:53 +0200 Subject: [PATCH 023/182] check if tmp is null --- src/util/Misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/Misc.h b/src/util/Misc.h index 6cd667d..6bac94b 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -119,7 +119,7 @@ inline std::string getHomeDir() { inline std::string getTmpDir() { // first, check if an env variable is set const char* tmpdir = getenv("TMPDIR"); - if (std::strlen(tmpdir)) return std::string(tmpdir); + if (tmpdir && std::strlen(tmpdir)) return std::string(tmpdir); // second, check if /tmp is writable if (access("/tmp/", W_OK) == 0) return "/tmp"; From 79303f12f232f3e565860191977a39538b214db6 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 28 Jun 2019 15:10:15 +0200 Subject: [PATCH 024/182] update /util --- src/pfaedle/PfaedleMain.cpp | 24 +- src/util/geo/BezierCurve.h | 3 +- src/util/geo/Geo.h | 138 +- src/util/tests/TestMain.cpp | 2763 +++++++++++++++++++---------------- src/util/tests/lest.h | 145 +- 5 files changed, 1719 insertions(+), 1354 deletions(-) diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 6a81f88..4c6e309 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -32,23 +32,11 @@ #include "util/log/Log.h" #include "util/Misc.h" -#ifndef HOME_VAR -#define HOME_VAR "HOME" +#ifndef CFG_HOME_SUFFIX +#define CFG_HOME_SUFFIX "/.config" #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" +#ifndef CFG_DIR +#define CFG_DIR "/etc" #endif #ifndef CFG_FILE_NAME #define CFG_FILE_NAME "pfaedle.cfg" @@ -343,7 +331,7 @@ std::vector getCfgPaths(const Config& cfg) { // install prefix global configuration path, if available { auto path = std::string(INSTALL_PREFIX) + - std::string(XDG_CONFIG_DIRS_DEFAULT) + "/" + "pfaedle" + "/" + + std::string(CFG_DIR) + "/" + "pfaedle" + "/" + CFG_FILE_NAME; std::ifstream is(path); @@ -356,7 +344,7 @@ std::vector getCfgPaths(const Config& cfg) { // local user configuration path, if available { - auto path = util::getHomeDir() + XDG_CONFIG_HOME_SUFFIX + "/" + + auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" + CFG_FILE_NAME; std::ifstream is(path); diff --git a/src/util/geo/BezierCurve.h b/src/util/geo/BezierCurve.h index f3d25c5..23f3263 100644 --- a/src/util/geo/BezierCurve.h +++ b/src/util/geo/BezierCurve.h @@ -27,7 +27,8 @@ struct CubicPolynom { template class BezierCurve { public: - BezierCurve(const Point& a, const Point& b, const Point& c, const Point& d); + BezierCurve(const Point& a, const Point& b, const Point& c, + const Point& d); const PolyLine& render(double d); diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index 679d9cd..1cb696e 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -243,15 +243,21 @@ inline bool doubleEq(double a, double b) { return fabs(a - b) < EPSILON; } // _____________________________________________________________________________ template inline bool contains(const Point& p, const Box& box) { - return p.getX() >= box.getLowerLeft().getX() && - p.getX() <= box.getUpperRight().getX() && - p.getY() >= box.getLowerLeft().getY() && - p.getY() <= box.getUpperRight().getY(); + // check if point lies in box + return (fabs(p.getX() - box.getLowerLeft().getX()) < EPSILON || + p.getX() > box.getLowerLeft().getX()) && + (fabs(p.getX() - box.getUpperRight().getX()) < EPSILON || + p.getX() < box.getUpperRight().getX()) && + (fabs(p.getY() - box.getLowerLeft().getY()) < EPSILON || + p.getY() > box.getLowerLeft().getY()) && + (fabs(p.getY() - box.getUpperRight().getY()) < EPSILON || + p.getY() < box.getUpperRight().getY()); } // _____________________________________________________________________________ template inline bool contains(const Line& l, const Box& box) { + // check if line lies in box for (const auto& p : l) if (!contains(p, box)) return false; return true; @@ -260,24 +266,35 @@ inline bool contains(const Line& l, const Box& box) { // _____________________________________________________________________________ template inline bool contains(const LineSegment& l, const Box& box) { + // check if line segment lies in box return contains(l.first, box) && contains(l.second, box); } // _____________________________________________________________________________ template inline bool contains(const Box& b, const Box& box) { + // check if box b lies in box return contains(b.getLowerLeft(), box) && contains(b.getUpperRight(), box); } // _____________________________________________________________________________ template inline bool contains(const Point& p, const LineSegment& ls) { + // check if point p lies in (on) line segment ls return fabs(crossProd(p, ls)) < EPSILON && contains(p, getBoundingBox(ls)); } +// _____________________________________________________________________________ +template +inline bool contains(const LineSegment& a, const LineSegment& b) { + // check if line segment a is contained in line segment b + return contains(a.first, b) && contains(a.second, b); +} + // _____________________________________________________________________________ template inline bool contains(const Point& p, const Line& l) { + // check if point p lies in line l for (size_t i = 1; i < l.size(); i++) { if (contains(p, LineSegment(l[i - 1], l[i]))) return true; } @@ -287,6 +304,8 @@ inline bool contains(const Point& p, const Line& l) { // _____________________________________________________________________________ template inline bool contains(const Point& p, const Polygon& poly) { + // check if point p lies in polygon + // see https://de.wikipedia.org/wiki/Punkt-in-Polygon-Test_nach_Jordan int8_t c = -1; @@ -328,19 +347,53 @@ inline int8_t polyContCheck(const Point& a, Point b, Point c) { // _____________________________________________________________________________ template inline bool contains(const Polygon& polyC, const Polygon& poly) { - for (const auto& p : polyC.getOuter()) { - if (!contains(p, poly)) { + // check if polygon polyC lies in polygon poly + + for (size_t i = 1; i < polyC.getOuter().size(); i++) { + if (!contains(LineSegment(polyC.getOuter()[i - 1], polyC.getOuter()[i]), + poly)) + return false; + } + + // also check the last hop + if (!contains(LineSegment(polyC.getOuter().back(), polyC.getOuter().front()), + poly)) + return false; + + return true; +} + +// _____________________________________________________________________________ +template +inline bool contains(const LineSegment& ls, const Polygon& p) { + // check if linesegment ls lies in polygon poly + + // if one of the endpoints lies outside, abort + if (!contains(ls.first, p)) return false; + if (!contains(ls.second, p)) return false; + + for (size_t i = 1; i < p.getOuter().size(); i++) { + auto seg = LineSegment(p.getOuter()[i - 1], p.getOuter()[i]); + if (!(contains(ls.first, seg) || contains(ls.second, seg)) && + intersects(seg, ls)) { return false; } } + + auto seg = LineSegment(p.getOuter().back(), p.getOuter().front()); + if (!(contains(ls.first, seg) || contains(ls.second, seg)) && + intersects(seg, ls)) { + return false; + } + return true; } // _____________________________________________________________________________ template inline bool contains(const Line& l, const Polygon& poly) { - for (const auto& p : l) { - if (!contains(p, poly)) { + for (size_t i = 1; i < l.size(); i++) { + if (!contains(LineSegment(l[i - 1], l[i]), poly)) { return false; } } @@ -351,9 +404,7 @@ inline bool contains(const Line& l, const Polygon& poly) { template inline bool contains(const Line& l, const Line& other) { for (const auto& p : l) { - if (!contains(p, other)) { - return false; - } + if (!contains(p, other)) return false; } return true; } @@ -361,17 +412,13 @@ inline bool contains(const Line& l, const Line& other) { // _____________________________________________________________________________ template inline bool contains(const Box& b, const Polygon& poly) { - return contains(b.getLowerLeft(), poly) && - contains(b.getUpperRight(), poly) && - contains(Point(b.getUpperRight().getX(), b.getLowerLeft().getY()), - poly) && - contains(Point(b.getLowerLeft().getX(), b.getUpperRight().getY()), - poly); + return contains(convexHull(b), poly); } // _____________________________________________________________________________ template inline bool contains(const Polygon& poly, const Box& b) { + // check of poly lies in box for (const auto& p : poly.getOuter()) { if (!contains(p, b)) return false; } @@ -400,6 +447,8 @@ inline bool contains(const std::vector>& multigeo, // _____________________________________________________________________________ template inline bool intersects(const LineSegment& ls1, const LineSegment& ls2) { + // check if two linesegments intersect + // two line segments intersect of there is a single, well-defined intersection // point between them. If more than 1 endpoint is colinear with any line, // the segments have infinite intersections. We handle this case as non- @@ -468,6 +517,39 @@ inline bool intersects(const Box& b1, const Box& b2) { b1.getUpperRight().getY() >= b2.getLowerLeft().getY(); } +// _____________________________________________________________________________ +template +inline bool intersects(const Box& b, const Polygon& poly) { + return intersects(b, poly); +} + +// _____________________________________________________________________________ +template +inline bool intersects(const Polygon& poly, const Box& b) { + if (intersects( + LineSegment(b.getLowerLeft(), Point(b.getUpperRight().getX(), + b.getLowerLeft().getY())), + poly)) + return true; + if (intersects( + LineSegment(b.getLowerLeft(), Point(b.getLowerLeft().getX(), + b.getUpperRight().getY())), + poly)) + return true; + if (intersects( + LineSegment(b.getUpperRight(), Point(b.getLowerLeft().getX(), + b.getUpperRight().getY())), + poly)) + return true; + if (intersects( + LineSegment(b.getUpperRight(), Point(b.getUpperRight().getX(), + b.getLowerLeft().getY())), + poly)) + return true; + + return contains(poly, b) || contains(b, poly); +} + // _____________________________________________________________________________ template inline bool intersects(const LineSegment& ls, const Box& b) { @@ -499,6 +581,7 @@ inline bool intersects(const LineSegment& ls, const Polygon& p) { return true; } + // also check the last hop if (intersects(LineSegment(p.getOuter().back(), p.getOuter().front()), ls)) return true; @@ -1186,7 +1269,7 @@ inline Polygon convexHull(const RotatedBox& b) { // _____________________________________________________________________________ template inline size_t convexHullImpl(const MultiPoint& a, size_t p1, size_t p2, - Line* h, uint8_t d) { + Line* h) { // quickhull by Barber, Dobkin & Huhdanpaa Point pa; bool found = false; @@ -1194,7 +1277,7 @@ inline size_t convexHullImpl(const MultiPoint& a, size_t p1, size_t p2, for (const auto& p : a) { double tmpDist = distToSegment((*h)[p1], (*h)[p2], p); double cp = crossProd(p, LineSegment((*h)[p1], (*h)[p2])); - if (((cp > 0 && !d) || (cp < 0 && d)) && tmpDist >= maxDist + EPSILON) { + if ((cp > 0) && tmpDist > maxDist) { pa = p; found = true; maxDist = tmpDist; @@ -1203,9 +1286,9 @@ inline size_t convexHullImpl(const MultiPoint& a, size_t p1, size_t p2, if (!found) return 0; - h->insert(h->begin() + p2 + !d, pa); - size_t in = 1 + convexHullImpl(a, p1, p2 + !d, h, d); - return in + convexHullImpl(a, p2 + in * d + 1 - 2 * d, p2 + in * d, h, d); + h->insert(h->begin() + p2, pa); + size_t in = 1 + convexHullImpl(a, p1, p2, h); + return in + convexHullImpl(a, p2 + in - 1, p2 + in, h); } // _____________________________________________________________________________ @@ -1217,13 +1300,16 @@ inline Polygon convexHull(const MultiPoint& l) { Point left(std::numeric_limits::max(), 0); Point right(std::numeric_limits::lowest(), 0); for (const auto& p : l) { - if (p.getX() <= left.getX()) left = p; - if (p.getX() >= right.getX()) right = p; + if (p.getX() < left.getX()) left = p; + if (p.getX() > right.getX()) right = p; } Line hull{left, right}; - convexHullImpl(l, 0, 1, &hull, 1); - convexHullImpl(l, 0, hull.size() - 1, &hull, 0); + convexHullImpl(l, 0, 1, &hull); + hull.push_back(hull.front()); + convexHullImpl(l, hull.size() - 2, hull.size() - 1, &hull); + hull.pop_back(); + return Polygon(hull); } diff --git a/src/util/tests/TestMain.cpp b/src/util/tests/TestMain.cpp index d7590d3..f03285b 100644 --- a/src/util/tests/TestMain.cpp +++ b/src/util/tests/TestMain.cpp @@ -3,1328 +3,1400 @@ // #include -#include "lest.h" +#include "util/Misc.h" #include "util/Nullable.h" #include "util/String.h" #include "util/geo/Geo.h" -#include "util/json/Writer.h" -#include "util/graph/Algorithm.h" -#include "util/graph/DirGraph.h" -#include "util/graph/UndirGraph.h" -#include "util/graph/Dijkstra.h" -#include "util/graph/EDijkstra.h" #include "util/geo/Grid.h" -#include "util/Misc.h" +#include "util/graph/Algorithm.h" +#include "util/graph/Dijkstra.h" +#include "util/graph/DirGraph.h" +#include "util/graph/EDijkstra.h" +#include "util/graph/UndirGraph.h" +#include "util/json/Writer.h" -using lest::approx; using namespace util; using namespace util::geo; using namespace util::graph; -// define LEST cases -const lest::test specification[] = { +class approx { + public: + explicit approx(double magnitude) + : _epsilon{std::numeric_limits::epsilon() * 100}, + _magnitude{magnitude} {} -// ___________________________________________________________________________ -{ -CASE("atof") { - EXPECT(util::atof("45.534215") == approx(45.534215)); - EXPECT(util::atof("5.534") == approx(5.534)); - EXPECT(util::atof("534") == approx(534)); - EXPECT(util::atof("-534") == approx(-534)); - EXPECT(util::atof("-45.534215") == approx(-45.534215)); - EXPECT(util::atof("-45.534215", 2) == approx(-45.53)); - - - // TODO: more test cases -}}, - -// ___________________________________________________________________________ -{ -CASE("json") { - std::stringstream ss; - util::json::Writer wr(&ss, 2, false); - - util::json::Val a("bla"); - util::json::Val b(1); - util::json::Val c(1.0); - util::json::Val d("a"); - util::json::Val e({"a", "b", "c"}); - - util::json::Val f({1, json::Array{2, 3, 4}, 3}); - - ss.str(""); - wr = util::json::Writer(&ss, 2, false); - util::json::Val i({1, json::Array{2, json::Null(), 4}, true}); - wr.val(i); - wr.closeAll(); - EXPECT(ss.str() == "[1,[2,null,4],true]"); - - ss.str(""); - wr = util::json::Writer(&ss, 2, false); - i = util::json::Val({1, json::Array{2, json::Null(), 4}, false}); - wr.val(i); - wr.closeAll(); - EXPECT(ss.str() == "[1,[2,null,4],false]"); - - ss.str(""); - wr = util::json::Writer(&ss, 2, false); - i = util::json::Val({1, json::Array{2, json::Null(), 4}, false}); - wr.val(i); - wr.closeAll(); - EXPECT(ss.str() == "[1,[2,null,4],false]"); - - ss.str(""); - wr = util::json::Writer(&ss, 2, false); - i = util::json::Val({1, json::Array{2.13, "", 4}, 0}); - wr.val(i); - wr.closeAll(); - EXPECT(ss.str() == "[1,[2.13,\"\",4],0]"); - - ss.str(""); - wr = util::json::Writer(&ss, 2, false); - i = util::json::Val({1, json::Array{2.13, json::Dict{{"a", 1}, {"B", 2.123}}, 4}, 0}); - wr.val(i); - wr.closeAll(); - EXPECT((ss.str() == "[1,[2.13,{\"a\":1,\"B\":2.12},4],0]" || ss.str() == "[1,[2.13,{\"B\":2.12,\"a\":1},4],0]")); -}}, - -// ___________________________________________________________________________ -{ -CASE("dirgraph") { - DirGraph g; - - DirNode* a = new DirNode(0); - DirNode* b = new DirNode(0); - g.addNd(a); - EXPECT(g.getNds()->size() == (size_t)1); - g.addNd(b); - EXPECT(g.getNds()->size() == (size_t)2); - - g.addEdg(a, b); - EXPECT(a->getDeg() == (size_t)1); - EXPECT(b->getDeg() == (size_t)0); - - auto c = g.addNd(); - - g.addEdg(a, c); - g.addEdg(c, b); - EXPECT(a->getDeg() == (size_t)2); - EXPECT(b->getDeg() == (size_t)0); - EXPECT(c->getDeg() == (size_t)1); - - g.delEdg(a, c); - - EXPECT(a->getDeg() == (size_t)1); - EXPECT(b->getDeg() == (size_t)0); - EXPECT(c->getDeg() == (size_t)1); - - g.addEdg(a, a); - EXPECT(a->getDeg() == (size_t)2); - - g.delEdg(a, a); - EXPECT(a->getDeg() == (size_t)1); - - g.delEdg(a, a); - EXPECT(a->getDeg() == (size_t)1); - - // TODO: more test cases -}}, - -// ___________________________________________________________________________ -{ -CASE("unddirgraph") { - UndirGraph g; - - UndirNode* a = new UndirNode(0); - UndirNode* b = new UndirNode(0); - g.addNd(a); - EXPECT(g.getNds()->size() == (size_t)1); - g.addNd(b); - EXPECT(g.getNds()->size() == (size_t)2); - - g.addEdg(a, b); - EXPECT(a->getDeg() == (size_t)1); - EXPECT(b->getDeg() == (size_t)1); - - auto c = g.addNd(); - - g.addEdg(a, c); - g.addEdg(c, b); - EXPECT(a->getDeg() == (size_t)2); - EXPECT(b->getDeg() == (size_t)2); - EXPECT(c->getDeg() == (size_t)2); - - g.delEdg(a, c); - - EXPECT(a->getDeg() == (size_t)1); - EXPECT(b->getDeg() == (size_t)2); - EXPECT(c->getDeg() == (size_t)1); - - g.delNd(b); - - EXPECT(a->getDeg() == (size_t)0); - EXPECT(c->getDeg() == (size_t)0); - - g.addEdg(a, a); - EXPECT(a->getDeg() == (size_t)1); - - g.delEdg(a, a); - EXPECT(a->getDeg() == (size_t)0); - - // TODO: more test cases - -}}, - -// ___________________________________________________________________________ -{ -CASE("grid") { - Grid g(.5, .5, Box(Point(0, 0), Point(3, 3))); - - Line l; - l.push_back(Point(0, 0)); - l.push_back(Point(1.5, 2)); - - Line l2; - l2.push_back(Point(2.5, 1)); - l2.push_back(Point(2.5, 2)); - - g.add(l, 1); - g.add(l2, 2); - - std::set ret; - - Box req(Point(.5, 1), Point(1, 1.5)); - g.get(req, &ret); - EXPECT(ret.size() == (size_t)1); - - ret.clear(); - g.getNeighbors(1, 0, &ret); - EXPECT(ret.size() == (size_t)1); - - ret.clear(); - g.getNeighbors(1, 0.55, &ret); - EXPECT(ret.size() == (size_t)2); - - - // TODO: more test cases -}}, - -// ___________________________________________________________________________ -{ -CASE("densify") { - Line a; - a.push_back(Point(1, 1)); - a.push_back(Point(10, 1)); - - auto dense = util::geo::densify(a, 1); - - EXPECT(dense.size() == (size_t)10); - - for (int i = 0; i < 10; i++) { - EXPECT(dense[i].getX() == approx(i + 1.0)); + friend bool operator==(double lhs, approx const& rhs) { + return std::abs(lhs - rhs._magnitude) < rhs._epsilon; } - dense = util::geo::simplify(dense, 0.1); - EXPECT(dense.size() == (size_t)2); - - Line b; - b.push_back(Point(1, 1)); - b.push_back(Point(5, 7)); - b.push_back(Point(10, 3)); - - dense = util::geo::densify(b, 1); - - dense = util::geo::simplify(dense, 0.1); - EXPECT(dense.size() == (size_t)3); -}}, - -// ___________________________________________________________________________ -{ -CASE("summed frechet distance") { - Line a; - a.push_back(Point(1, 1)); - a.push_back(Point(2, 1)); - a.push_back(Point(3, 1)); - a.push_back(Point(3, 2)); - a.push_back(Point(4, 2)); - a.push_back(Point(4, 1)); - a.push_back(Point(5, 1)); - a.push_back(Point(6, 1)); - - Line b; - b.push_back(Point(1, 1)); - b.push_back(Point(2, 1)); - b.push_back(Point(3, 1)); - b.push_back(Point(4, 1)); - b.push_back(Point(5, 1)); - b.push_back(Point(6, 1)); - - double fd = util::geo::accFrechetDistC(a, b, 0.1); - EXPECT(fd == approx(2)); -}}, - -// ___________________________________________________________________________ -{ -CASE("frechet distance") { - Line e; - e.push_back(Point(1, 1)); - e.push_back(Point(1, 2)); - - Line f; - f.push_back(Point(1, 1)); - f.push_back(Point(1, 2)); - - double fd = util::geo::frechetDist(e, f, 0.1); - - EXPECT(fd == approx(0)); - - Line a; - a.push_back(Point(1, 1)); - a.push_back(Point(2, 1)); - a.push_back(Point(3, 2)); - a.push_back(Point(4, 2)); - a.push_back(Point(5, 1)); - a.push_back(Point(6, 1)); - - Line b; - b.push_back(Point(1, 1)); - b.push_back(Point(2, 1)); - b.push_back(Point(3, 1)); - b.push_back(Point(4, 1)); - b.push_back(Point(5, 1)); - b.push_back(Point(6, 1)); - - auto adense = util::geo::densify(a, 0.1); - auto bdense = util::geo::densify(b, 0.1); - - fd = util::geo::frechetDist(a, b, 0.1); - - EXPECT(fd == approx(1)); - - Line c; - c.push_back(Point(1, 1)); - c.push_back(Point(2, 1)); - - Line d; - d.push_back(Point(3, 1)); - d.push_back(Point(4, 1)); - - fd = util::geo::frechetDist(c, d, 0.1); - - EXPECT(fd == approx(2)); - - - Line g; - g.push_back(Point(1, 1)); - g.push_back(Point(10, 1)); - - Line h; - h.push_back(Point(1, 1)); - h.push_back(Point(3, 2)); - h.push_back(Point(3, 1)); - h.push_back(Point(10, 1)); - - fd = util::geo::frechetDist(g, h, 0.1); - - EXPECT(fd == approx(1)); -}}, - -// ___________________________________________________________________________ -{ -CASE("geo box alignment") { - Line a; - a.push_back(Point(1, 1)); - a.push_back(Point(1, 2)); - - Line b; - b.push_back(Point(1, 2)); - b.push_back(Point(2, 2)); - - Line c; - c.push_back(Point(2, 2)); - c.push_back(Point(2, 1)); - - Line d; - d.push_back(Point(2, 1)); - d.push_back(Point(1, 1)); - - Box box(Point(2, 3), Point(5, 4)); - MultiLine ml; - ml.push_back(a); - ml.push_back(b); - ml.push_back(c); - ml.push_back(d); - - EXPECT(parallelity(box, ml) == approx(1)); - ml = rotate(ml, 45); - EXPECT(parallelity(box, ml) == approx(0)); - ml = rotate(ml, 45); - EXPECT(parallelity(box, ml) == approx(1)); - ml = rotate(ml, 45); - EXPECT(parallelity(box, ml) == approx(0)); - ml = rotate(ml, 45); - EXPECT(parallelity(box, ml) == approx(1)); -}}, - -// ___________________________________________________________________________ -{ -CASE("url decode") { - EXPECT("zürich" == util::urlDecode("z%C3%BCrich")); - EXPECT("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()")); - EXPECT("Løkken" == util::urlDecode("L%C3%B8kken")); - EXPECT("á é" == util::urlDecode("%C3%A1%20%C3%A9")); - EXPECT("á é" == util::urlDecode("%C3%A1+%C3%A9")); -}}, - -// ___________________________________________________________________________ -{ -CASE("json escape") { - EXPECT("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!")); - EXPECT("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\"")); -}}, - -// ___________________________________________________________________________ -{ -CASE("split") { - EXPECT(util::split("hello,again", ',').size() == (size_t)2); - EXPECT(util::split("hello,,again", ',').size() == (size_t)3); - EXPECT(util::split("hello", ',').size() == (size_t)1); - EXPECT(util::split("", ',').size() == (size_t)0); -}}, - -// ___________________________________________________________________________ -{ -CASE("editdist") { - EXPECT(util::editDist("hello", "mello") == (size_t)1); - EXPECT(util::editDist("mello", "hello") == (size_t)1); - EXPECT(util::editDist("abcde", "abfde") == (size_t)1); - EXPECT(util::editDist("abcd", "abcde") == (size_t)1); - EXPECT(util::editDist("xabcd", "abcde") == (size_t)2); - EXPECT(util::editDist("abcd", "abcdes") == (size_t)2); - EXPECT(util::editDist("hello", "hello") == (size_t)0); -}}, - -// ___________________________________________________________________________ -{ -CASE("prefixeditdist") { - EXPECT(util::prefixEditDist("hello", "hello", 0) == (size_t)0); - EXPECT(util::prefixEditDist("hello", "hello", 100) == (size_t)0); - EXPECT(util::prefixEditDist("hello", "hello") == (size_t)0); - EXPECT(util::prefixEditDist("hel", "hello") == (size_t)0); - EXPECT(util::prefixEditDist("hel", "hello", 0) == (size_t)0); - EXPECT(util::prefixEditDist("hel", "hello", 1) == (size_t)0); - EXPECT(util::prefixEditDist("hel", "hello", 2) == (size_t)0); - EXPECT(util::prefixEditDist("hal", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("hal", "hello", 1) == (size_t)1); - EXPECT(util::prefixEditDist("hal", "hello", 0) > (size_t)0); - EXPECT(util::prefixEditDist("fel", "hello", 0) > (size_t)0); - EXPECT(util::prefixEditDist("fel", "hello", 1) == (size_t)1); - EXPECT(util::prefixEditDist("fel", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("fal", "hello", 2) == (size_t)2); - EXPECT(util::prefixEditDist("fal", "hello", 1) > (size_t)1); - EXPECT(util::prefixEditDist("fal", "hello", 0) > (size_t)0); - EXPECT(util::prefixEditDist("far", "hello", 0) > (size_t)0); - EXPECT(util::prefixEditDist("far", "hello", 1) > (size_t)1); - EXPECT(util::prefixEditDist("far", "hello", 2) > (size_t)2); - EXPECT(util::prefixEditDist("far", "hello", 3) == (size_t)3); - EXPECT(util::prefixEditDist("far", "hello", 4) == (size_t)3); - EXPECT(util::prefixEditDist("far", "hello") == (size_t)3); - EXPECT(util::prefixEditDist("hefar", "hello") == (size_t)3); - EXPECT(util::prefixEditDist("hefaree", "hello") == (size_t)5); - EXPECT(util::prefixEditDist("helloo", "hello") == (size_t)1); - EXPECT(util::prefixEditDist("helloo", "hello", 0) > (size_t)0); - EXPECT(util::prefixEditDist("helloo", "hello", 1) == (size_t)1); - EXPECT(util::prefixEditDist("helloo", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("", "hello", 2) == (size_t)0); - EXPECT(util::prefixEditDist("e", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("el", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("ello", "hello", 2) == (size_t)1); - EXPECT(util::prefixEditDist("hell", "hello", 2) == (size_t)0); - EXPECT(util::prefixEditDist("hell", "", 2) > (size_t)2); - EXPECT(util::prefixEditDist("hell", "") == (size_t)4); -}}, - -// ___________________________________________________________________________ -{ -CASE("toString") { - EXPECT(util::toString(34) == "34"); - EXPECT(util::toString("34") == "34"); -}}, - -// ___________________________________________________________________________ -{ -CASE("replace") { - std::string a("lorem ipsum ipsum lorem"); - - EXPECT(util::replace(a, "ips", "aa")); - EXPECT(a == "lorem aaum ipsum lorem"); - - EXPECT(!util::replace(a, "blablabla", "")); - EXPECT(a == "lorem aaum ipsum lorem"); - - EXPECT(util::replace(a, "m", "")); - EXPECT(a == "lore aaum ipsum lorem"); - - EXPECT(!util::replace(a, "", "")); - EXPECT(a == "lore aaum ipsum lorem"); - - std::string b("lorem ipsum ipsum lorem"); - EXPECT(util::replaceAll(b, "ips", "aa")); - EXPECT(b == "lorem aaum aaum lorem"); - - EXPECT(util::replaceAll(b, "m", "")); - EXPECT(b == "lore aau aau lore"); - - EXPECT(util::replaceAll(b, "a", "aa")); - EXPECT(b == "lore aaaau aaaau lore"); - - EXPECT(util::replaceAll(b, "e", "e")); - EXPECT(b == "lore aaaau aaaau lore"); - - EXPECT(util::replaceAll(b, "e", "ee")); - EXPECT(b == "loree aaaau aaaau loree"); - - EXPECT(!util::replaceAll(b, "", "ee")); - EXPECT(b == "loree aaaau aaaau loree"); -}}, - -// ___________________________________________________________________________ -{ -CASE("Connected components undirected") { - UndirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - g.addEdg(a, c, 1); - g.addEdg(a, b, 5); - g.addEdg(d, c, 1); - g.addEdg(d, b, 3); - g.addEdg(e, d, 1); - g.addEdg(e, b, 1); - - auto comps = util::graph::Algorithm::connectedComponents(g); - - EXPECT(comps.size() == static_cast(1)); - EXPECT(comps[0].count(a)); - EXPECT(comps[0].count(b)); - EXPECT(comps[0].count(c)); - EXPECT(comps[0].count(d)); - EXPECT(comps[0].count(e)); - - auto f = g.addNd("F"); - comps = util::graph::Algorithm::connectedComponents(g); - EXPECT(comps.size() == static_cast(2)); - - auto gn = g.addNd("G"); - comps = util::graph::Algorithm::connectedComponents(g); - EXPECT(comps.size() == static_cast(3)); - - g.addEdg(f, gn, 1); - comps = util::graph::Algorithm::connectedComponents(g); - EXPECT(comps.size() == static_cast(2)); - - g.addEdg(f, a, 1); - comps = util::graph::Algorithm::connectedComponents(g); - EXPECT(comps.size() == static_cast(1)); - -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra directed, 1 to all") { - DirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - auto eAC = g.addEdg(a, c, 1); - auto eAB = g.addEdg(a, b, 5); - auto eDC = g.addEdg(d, c, 1); - auto eDB = g.addEdg(d, b, 3); - auto eED = g.addEdg(e, d, 1); - auto eEB = g.addEdg(e, b, 1); - - - UNUSED(eAC); - UNUSED(eDC); - UNUSED(eDB); - UNUSED(eED); - UNUSED(eEB); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* from, - const Node* n, - const Edge* to) const { - UNUSED(from); - - // dont count cost of start edge - if (n) return to->pl(); - return 0; - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - auto cost = EDijkstra::shortestPath(eAB, cFunc); - - for (auto u : cost) { - int single = EDijkstra::shortestPath(eAB, u.first, cFunc); - EXPECT(single == u.second); + friend bool operator==(approx const& lhs, double rhs) { + return operator==(rhs, lhs); + } + friend bool operator!=(double lhs, approx const& rhs) { + return !operator==(lhs, rhs); + } + friend bool operator!=(approx const& lhs, double rhs) { + return !operator==(rhs, lhs); } - // all to 1 - auto eBC = g.addEdg(b, c, 10); - - auto costb = EDijkstra::shortestPathRev(eBC, cFunc); - for (auto u : costb) { - int single = EDijkstra::shortestPath(u.first, eBC, cFunc); - EXPECT(single == u.second); + friend bool operator<=(double lhs, approx const& rhs) { + return lhs < rhs._magnitude || lhs == rhs; } -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra undirected, edge 1 to 1") { - UndirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - auto eAC = g.addEdg(a, c, 1); - auto eAB = g.addEdg(a, b, 5); - auto eDC = g.addEdg(d, c, 1); - auto eDB = g.addEdg(d, b, 3); - auto eED = g.addEdg(e, d, 1); - auto eEB = g.addEdg(e, b, 1); - - UNUSED(eAC); - UNUSED(eDC); - UNUSED(eDB); - UNUSED(eED); - UNUSED(eEB); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* from, - const Node* n, - const Edge* to) const { - UNUSED(from); - - // dont count cost of start edge - if (n) return to->pl(); - return 0; - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - EDijkstra::NList res; - EDijkstra::EList resE; - int cost = EDijkstra::shortestPath(eAB, d, cFunc, &resE, &res); - - EXPECT(cost == 2); - - EXPECT(resE.size() == (size_t)3); - EXPECT(res.size() == (size_t)3); - EXPECT((*(res.rbegin()))->pl() == "A"); - EXPECT((*(++res.rbegin()))->pl() == "C"); - EXPECT((*(++++res.rbegin()))->pl() == "D"); - - EXPECT((*(resE.rbegin())) == eAB); - EXPECT((*(++resE.rbegin())) == eAC); - EXPECT((*(++++resE.rbegin())) == eDC); - - cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res); - EXPECT(cost == 0); -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra undirected, edge 1 to n") { - UndirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - auto eAC = g.addEdg(a, c, 1); - auto eAB = g.addEdg(a, b, 5); - auto eDC = g.addEdg(d, c, 1); - auto eDB = g.addEdg(d, b, 3); - auto eED = g.addEdg(e, d, 1); - auto eEB = g.addEdg(e, b, 1); - - UNUSED(eAC); - UNUSED(eDC); - UNUSED(eDB); - UNUSED(eED); - UNUSED(eEB); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* from, const Node* n, - const Edge* to) const { - UNUSED(from); - - // dont count cost of start edge - if (n) return to->pl(); - return 0; - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - std::set*> tos; - tos.insert(d); - tos.insert(b); - tos.insert(b); - - EDijkstra::NList res; - EDijkstra::EList resE; - int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res); - EXPECT(cost == 0); -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra undirected, 1 to n") { - UndirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - g.addEdg(a, c, 1); - auto eAB = g.addEdg(a, b, 5); - auto eDC = g.addEdg(d, c, 1); - g.addEdg(d, b, 3); - auto eED = g.addEdg(e, d, 1); - g.addEdg(e, b, 1); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* from, const Node* n, - const Edge* to) const { - UNUSED(from); - - // dont count cost of start edge - if (n) return to->pl(); - return 0; - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - std::set*> tos; - tos.insert(eDC); - tos.insert(eED); - - std::unordered_map*, EDijkstra::EList*> resE; - resE[eDC] = new EDijkstra::EList(); - resE[eED] = new EDijkstra::EList(); - std::unordered_map*, EDijkstra::NList*> res; - res[eDC] = new EDijkstra::NList(); - res[eED] = new EDijkstra::NList(); - auto hFunc = EDijkstra::ZeroHeurFunc(); - std::unordered_map*, int> cost = EDijkstra::shortestPath(eAB, tos, cFunc, hFunc, resE, res); - - EXPECT(cost[eDC] == 2); - EXPECT(cost[eED] == 2); - - EXPECT(resE[eDC]->size() == (size_t)3); - EXPECT(res[eED]->size() == (size_t)3); - - EXPECT(resE[eDC]->size() == (size_t)3); - EXPECT(res[eED]->size() == (size_t)3); -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra undirected") { - UndirGraph g; - - auto a = g.addNd("A"); - auto b = g.addNd("B"); - auto c = g.addNd("C"); - auto d = g.addNd("D"); - auto e = g.addNd("E"); - - g.addEdg(a, c, 1); - g.addEdg(a, b, 5); - g.addEdg(d, c, 1); - g.addEdg(d, b, 3); - g.addEdg(e, d, 1); - g.addEdg(e, b, 1); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* fr, const Node* n, - const Edge* to) const { - UNUSED(fr); - UNUSED(n); - return to->pl(); - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - EDijkstra::NList res; - EDijkstra::EList resE; - int cost = EDijkstra::shortestPath(a, b, cFunc, &resE, &res); - - EXPECT(res.size() == (size_t)5); - EXPECT((*(res.rbegin()))->pl() == "A"); - EXPECT((*(++res.rbegin()))->pl() == "C"); - EXPECT((*(++++res.rbegin()))->pl() == "D"); - EXPECT((*(++++++res.rbegin()))->pl() == "E"); - EXPECT((*(++++++++res.rbegin()))->pl() == "B"); - EXPECT(cost == 4); - EXPECT((*(resE.rbegin()))->getFrom()->pl() == "A"); - EXPECT((*(++resE.rbegin()))->getFrom()->pl() == "D"); - EXPECT((*(++++resE.rbegin()))->getFrom()->pl() == "E"); - EXPECT((*(++++++resE.rbegin()))->getTo()->pl() == "B"); - - EXPECT(resE.size() == (size_t)4); - - cost = EDijkstra::shortestPath(d, b, cFunc, &res); - EXPECT(cost == 2); - - cost = EDijkstra::shortestPath(b, d, cFunc, &res); - EXPECT(cost == 2); - - cost = EDijkstra::shortestPath(e, b, cFunc, &res); - EXPECT(cost == 1); - - cost = EDijkstra::shortestPath(b, e, cFunc, &res); - EXPECT(cost == 1); - - cost = EDijkstra::shortestPath(b, a, cFunc, &res); - EXPECT(cost == 4); - - cost = EDijkstra::shortestPath(c, a, cFunc, &res); - EXPECT(cost == 1); - - cost = EDijkstra::shortestPath(a, c, cFunc, &res); - EXPECT(cost == 1); - - cost = EDijkstra::shortestPath(a, d, cFunc, &res); - EXPECT(cost == 2); -}}, - -// ___________________________________________________________________________ -{ -CASE("Edge-based Dijkstra") { - DirGraph g; - - DirNode* a = new DirNode(1); - DirNode* b = new DirNode(4); - g.addNd(a); - g.addNd(b); - - auto c = g.addNd(2); - auto d = g.addNd(3); - auto x = g.addNd(); - - g.addEdg(a, d, 4); - g.addEdg(a, c, 1); - g.addEdg(c, b, 1); - g.addEdg(b, d, 1); - - struct CostFunc : public EDijkstra::CostFunc { - int operator()(const Edge* fr, const Node* n, - const Edge* to) const { - UNUSED(fr); - UNUSED(n); - return to->pl(); - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - EDijkstra::NList res; - int cost = EDijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 3); - - g.addEdg(c, d, 3); - cost = EDijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 3); - - g.addEdg(a, b, 1); - g.addEdg(x, a, 1); - cost = EDijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 2); -}}, - -// ___________________________________________________________________________ -{ -CASE("Dijkstra") { - DirGraph g; - - DirNode* a = new DirNode(1); - DirNode* b = new DirNode(0); - g.addNd(a); - g.addNd(b); - - auto c = g.addNd(); - auto d = g.addNd(4); - auto x = g.addNd(); - - g.addEdg(a, d, 4); - g.addEdg(a, c, 1); - g.addEdg(c, b, 1); - g.addEdg(b, d, 1); - - struct CostFunc : public Dijkstra::CostFunc { - int operator()(const Node* fr, const Edge* e, - const Node* to) const { - UNUSED(fr); - UNUSED(to); - return e->pl(); - }; - int inf() const { return 999; }; - }; - - CostFunc cFunc; - - Dijkstra::NList res; - int cost = Dijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 3); - EXPECT(res.size() == (size_t)4); - - g.addEdg(c, d, 3); - cost = Dijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 3); - - g.addEdg(a, b, 1); - g.addEdg(x, a, 1); - cost = Dijkstra::shortestPath(a, d, cFunc, &res); - - EXPECT(cost == 2); - - const std::set*> to{b, c, d, x}; - std::unordered_map*, Dijkstra::EList*> resEdges; - std::unordered_map*, Dijkstra::NList*> resNodes; - - for (auto n : to) { - resEdges[n] = new Dijkstra::EList(); - resNodes[n] = new Dijkstra::NList(); + friend bool operator<=(approx const& lhs, double rhs) { + return lhs._magnitude < rhs || lhs == rhs; + } + friend bool operator>=(double lhs, approx const& rhs) { + return lhs > rhs._magnitude || lhs == rhs; + } + friend bool operator>=(approx const& lhs, double rhs) { + return lhs._magnitude > rhs || lhs == rhs; } - auto costs = Dijkstra::shortestPath(a, to, cFunc, resEdges, resNodes); + private: + double _epsilon; + double _magnitude; +}; - EXPECT(costs[b] == 1); - EXPECT(costs[c] == 1); - EXPECT(costs[d] == 2); - EXPECT(costs[x] == 999); -}}, +// _____________________________________________________________________________ +int main(int argc, char** argv) { + UNUSED(argc); + UNUSED(argv); -// ___________________________________________________________________________ -{ -CASE("nullable") { + // ___________________________________________________________________________ { - util::Nullable nullable; - EXPECT(nullable.isNull()); + assert(util::atof("45.534215") == approx(45.534215)); + assert(util::atof("5.534") == approx(5.534)); + assert(util::atof("534") == approx(534)); + assert(util::atof("-534") == approx(-534)); + assert(util::atof("-45.534215") == approx(-45.534215)); + assert(util::atof("-45.534215", 2) == approx(-45.53)); + + // TODO: more test cases } + // ___________________________________________________________________________ { - util::Nullable nullable(0); - EXPECT(nullable.isNull()); + std::stringstream ss; + util::json::Writer wr(&ss, 2, false); + + util::json::Val a("bla"); + util::json::Val b(1); + util::json::Val c(1.0); + util::json::Val d("a"); + util::json::Val e({"a", "b", "c"}); + + util::json::Val f({1, json::Array{2, 3, 4}, 3}); + + ss.str(""); + wr = util::json::Writer(&ss, 2, false); + util::json::Val i({1, json::Array{2, json::Null(), 4}, true}); + wr.val(i); + wr.closeAll(); + assert(ss.str() == "[1,[2,null,4],true]"); + + ss.str(""); + wr = util::json::Writer(&ss, 2, false); + i = util::json::Val({1, json::Array{2, json::Null(), 4}, false}); + wr.val(i); + wr.closeAll(); + assert(ss.str() == "[1,[2,null,4],false]"); + + ss.str(""); + wr = util::json::Writer(&ss, 2, false); + i = util::json::Val({1, json::Array{2, json::Null(), 4}, false}); + wr.val(i); + wr.closeAll(); + assert(ss.str() == "[1,[2,null,4],false]"); + + ss.str(""); + wr = util::json::Writer(&ss, 2, false); + i = util::json::Val({1, json::Array{2.13, "", 4}, 0}); + wr.val(i); + wr.closeAll(); + assert(ss.str() == "[1,[2.13,\"\",4],0]"); + + ss.str(""); + wr = util::json::Writer(&ss, 2, false); + i = util::json::Val( + {1, json::Array{2.13, json::Dict{{"a", 1}, {"B", 2.123}}, 4}, 0}); + wr.val(i); + wr.closeAll(); + assert((ss.str() == "[1,[2.13,{\"a\":1,\"B\":2.12},4],0]" || + ss.str() == "[1,[2.13,{\"B\":2.12,\"a\":1},4],0]")); } + // ___________________________________________________________________________ { - std::string str = "aa"; - util::Nullable nullable(&str); - EXPECT(!nullable.isNull()); + DirGraph g; - EXPECT(nullable == "aa"); - EXPECT(!(nullable == "aaa")); - EXPECT(!(nullable != "aa")); - EXPECT(nullable == "aa"); + DirNode* a = new DirNode(0); + DirNode* b = new DirNode(0); + g.addNd(a); + assert(g.getNds()->size() == (size_t)1); + g.addNd(b); + assert(g.getNds()->size() == (size_t)2); - EXPECT(nullable.get() == "aa"); - EXPECT(std::string(nullable) == "aa"); + g.addEdg(a, b); + assert(a->getDeg() == (size_t)1); + assert(b->getDeg() == (size_t)0); + + auto c = g.addNd(); + + g.addEdg(a, c); + g.addEdg(c, b); + assert(a->getDeg() == (size_t)2); + assert(b->getDeg() == (size_t)0); + assert(c->getDeg() == (size_t)1); + + g.delEdg(a, c); + + assert(a->getDeg() == (size_t)1); + assert(b->getDeg() == (size_t)0); + assert(c->getDeg() == (size_t)1); + + g.addEdg(a, a); + assert(a->getDeg() == (size_t)2); + + g.delEdg(a, a); + assert(a->getDeg() == (size_t)1); + + g.delEdg(a, a); + assert(a->getDeg() == (size_t)1); + + // TODO: more test cases } + // ___________________________________________________________________________ { - int a = 23; - util::Nullable nullable(a); - util::Nullable nullable2(24); - EXPECT(!nullable.isNull()); + UndirGraph g; - EXPECT(nullable == 23); - EXPECT(nullable >= 23); - EXPECT(nullable <= 23); - EXPECT(nullable < 24); - EXPECT(nullable < 24); - EXPECT(!(nullable < 22)); - EXPECT(nullable != nullable2); - EXPECT(nullable < nullable2); - EXPECT(nullable2 > nullable); + UndirNode* a = new UndirNode(0); + UndirNode* b = new UndirNode(0); + g.addNd(a); + assert(g.getNds()->size() == (size_t)1); + g.addNd(b); + assert(g.getNds()->size() == (size_t)2); - util::Nullable nullable3(nullable); - EXPECT(nullable == nullable3); + g.addEdg(a, b); + assert(a->getDeg() == (size_t)1); + assert(b->getDeg() == (size_t)1); - nullable3 = nullable2; - EXPECT(nullable2 == nullable3); - EXPECT(nullable3 == 24); - EXPECT(nullable2 == 24); - EXPECT(nullable2 == nullable2.get()); - EXPECT(int(nullable2) == nullable2.get()); - EXPECT(!nullable3.isNull()); - EXPECT(!nullable2.isNull()); + auto c = g.addNd(); - util::Nullable voidnull; - EXPECT(voidnull.isNull()); + g.addEdg(a, c); + g.addEdg(c, b); + assert(a->getDeg() == (size_t)2); + assert(b->getDeg() == (size_t)2); + assert(c->getDeg() == (size_t)2); - EXPECT_THROWS(nullable == voidnull); + g.delEdg(a, c); + + assert(a->getDeg() == (size_t)1); + assert(b->getDeg() == (size_t)2); + assert(c->getDeg() == (size_t)1); + + g.delNd(b); + + assert(a->getDeg() == (size_t)0); + assert(c->getDeg() == (size_t)0); + + g.addEdg(a, a); + assert(a->getDeg() == (size_t)1); + + g.delEdg(a, a); + assert(a->getDeg() == (size_t)0); + + // TODO: more test cases } -}}, + + // ___________________________________________________________________________ + { + Grid g( + .5, .5, Box(Point(0, 0), Point(3, 3))); + + Line l; + l.push_back(Point(0, 0)); + l.push_back(Point(1.5, 2)); + + Line l2; + l2.push_back(Point(2.5, 1)); + l2.push_back(Point(2.5, 2)); + + g.add(l, 1); + g.add(l2, 2); + + std::set ret; + + Box req(Point(.5, 1), Point(1, 1.5)); + g.get(req, &ret); + assert(ret.size() == (size_t)1); + + ret.clear(); + g.getNeighbors(1, 0, &ret); + assert(ret.size() == (size_t)1); + + ret.clear(); + g.getNeighbors(1, 0.55, &ret); + assert(ret.size() == (size_t)2); + + // TODO: more test cases + } + + // ___________________________________________________________________________ + { + Line a; + a.push_back(Point(1, 1)); + a.push_back(Point(10, 1)); + + auto dense = util::geo::densify(a, 1); + + assert(dense.size() == (size_t)10); + + for (int i = 0; i < 10; i++) { + assert(dense[i].getX() == approx(i + 1.0)); + } + + dense = util::geo::simplify(dense, 0.1); + assert(dense.size() == (size_t)2); + + Line b; + b.push_back(Point(1, 1)); + b.push_back(Point(5, 7)); + b.push_back(Point(10, 3)); + + dense = util::geo::densify(b, 1); + + dense = util::geo::simplify(dense, 0.1); + assert(dense.size() == (size_t)3); + } + + // ___________________________________________________________________________ + { + Line a; + a.push_back(Point(1, 1)); + a.push_back(Point(2, 1)); + a.push_back(Point(3, 1)); + a.push_back(Point(3, 2)); + a.push_back(Point(4, 2)); + a.push_back(Point(4, 1)); + a.push_back(Point(5, 1)); + a.push_back(Point(6, 1)); + + Line b; + b.push_back(Point(1, 1)); + b.push_back(Point(2, 1)); + b.push_back(Point(3, 1)); + b.push_back(Point(4, 1)); + b.push_back(Point(5, 1)); + b.push_back(Point(6, 1)); + + double fd = util::geo::accFrechetDistC(a, b, 0.1); + assert(fd == approx(2)); + } + + // ___________________________________________________________________________ + { + Line e; + e.push_back(Point(1, 1)); + e.push_back(Point(1, 2)); + + Line f; + f.push_back(Point(1, 1)); + f.push_back(Point(1, 2)); + + double fd = util::geo::frechetDist(e, f, 0.1); + + assert(fd == approx(0)); + + Line a; + a.push_back(Point(1, 1)); + a.push_back(Point(2, 1)); + a.push_back(Point(3, 2)); + a.push_back(Point(4, 2)); + a.push_back(Point(5, 1)); + a.push_back(Point(6, 1)); + + Line b; + b.push_back(Point(1, 1)); + b.push_back(Point(2, 1)); + b.push_back(Point(3, 1)); + b.push_back(Point(4, 1)); + b.push_back(Point(5, 1)); + b.push_back(Point(6, 1)); + + auto adense = util::geo::densify(a, 0.1); + auto bdense = util::geo::densify(b, 0.1); + + fd = util::geo::frechetDist(a, b, 0.1); + + assert(fd == approx(1)); + + Line c; + c.push_back(Point(1, 1)); + c.push_back(Point(2, 1)); + + Line d; + d.push_back(Point(3, 1)); + d.push_back(Point(4, 1)); + + fd = util::geo::frechetDist(c, d, 0.1); + + assert(fd == approx(2)); + + Line g; + g.push_back(Point(1, 1)); + g.push_back(Point(10, 1)); + + Line h; + h.push_back(Point(1, 1)); + h.push_back(Point(3, 2)); + h.push_back(Point(3, 1)); + h.push_back(Point(10, 1)); + + fd = util::geo::frechetDist(g, h, 0.1); + + assert(fd == approx(1)); + } + + // ___________________________________________________________________________ + { + Line a; + a.push_back(Point(1, 1)); + a.push_back(Point(1, 2)); + + Line b; + b.push_back(Point(1, 2)); + b.push_back(Point(2, 2)); + + Line c; + c.push_back(Point(2, 2)); + c.push_back(Point(2, 1)); + + Line d; + d.push_back(Point(2, 1)); + d.push_back(Point(1, 1)); + + Box box(Point(2, 3), Point(5, 4)); + MultiLine ml; + ml.push_back(a); + ml.push_back(b); + ml.push_back(c); + ml.push_back(d); + + assert(parallelity(box, ml) == approx(1)); + ml = rotate(ml, 45); + assert(parallelity(box, ml) == approx(0)); + ml = rotate(ml, 45); + assert(parallelity(box, ml) == approx(1)); + ml = rotate(ml, 45); + assert(parallelity(box, ml) == approx(0)); + ml = rotate(ml, 45); + assert(parallelity(box, ml) == approx(1)); + } + + // ___________________________________________________________________________ + { + assert("zürich" == util::urlDecode("z%C3%BCrich")); + assert("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()")); + assert("Løkken" == util::urlDecode("L%C3%B8kken")); + assert("á é" == util::urlDecode("%C3%A1%20%C3%A9")); + assert("á é" == util::urlDecode("%C3%A1+%C3%A9")); + } + + // ___________________________________________________________________________ + { + assert("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!")); + assert("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\"")); + } + + // ___________________________________________________________________________ + { + assert(util::split("hello,again", ',').size() == (size_t)2); + assert(util::split("hello,,again", ',').size() == (size_t)3); + assert(util::split("hello", ',').size() == (size_t)1); + assert(util::split("", ',').size() == (size_t)0); + } + + // ___________________________________________________________________________ + { + assert(util::editDist("hello", "mello") == (size_t)1); + assert(util::editDist("mello", "hello") == (size_t)1); + assert(util::editDist("abcde", "abfde") == (size_t)1); + assert(util::editDist("abcd", "abcde") == (size_t)1); + assert(util::editDist("xabcd", "abcde") == (size_t)2); + assert(util::editDist("abcd", "abcdes") == (size_t)2); + assert(util::editDist("hello", "hello") == (size_t)0); + } + + // ___________________________________________________________________________ + { + assert(util::prefixEditDist("hello", "hello", 0) == (size_t)0); + assert(util::prefixEditDist("hello", "hello", 100) == (size_t)0); + assert(util::prefixEditDist("hello", "hello") == (size_t)0); + assert(util::prefixEditDist("hel", "hello") == (size_t)0); + assert(util::prefixEditDist("hel", "hello", 0) == (size_t)0); + assert(util::prefixEditDist("hel", "hello", 1) == (size_t)0); + assert(util::prefixEditDist("hel", "hello", 2) == (size_t)0); + assert(util::prefixEditDist("hal", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("hal", "hello", 1) == (size_t)1); + assert(util::prefixEditDist("hal", "hello", 0) > (size_t)0); + assert(util::prefixEditDist("fel", "hello", 0) > (size_t)0); + assert(util::prefixEditDist("fel", "hello", 1) == (size_t)1); + assert(util::prefixEditDist("fel", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("fal", "hello", 2) == (size_t)2); + assert(util::prefixEditDist("fal", "hello", 1) > (size_t)1); + assert(util::prefixEditDist("fal", "hello", 0) > (size_t)0); + assert(util::prefixEditDist("far", "hello", 0) > (size_t)0); + assert(util::prefixEditDist("far", "hello", 1) > (size_t)1); + assert(util::prefixEditDist("far", "hello", 2) > (size_t)2); + assert(util::prefixEditDist("far", "hello", 3) == (size_t)3); + assert(util::prefixEditDist("far", "hello", 4) == (size_t)3); + assert(util::prefixEditDist("far", "hello") == (size_t)3); + assert(util::prefixEditDist("hefar", "hello") == (size_t)3); + assert(util::prefixEditDist("hefaree", "hello") == (size_t)5); + assert(util::prefixEditDist("helloo", "hello") == (size_t)1); + assert(util::prefixEditDist("helloo", "hello", 0) > (size_t)0); + assert(util::prefixEditDist("helloo", "hello", 1) == (size_t)1); + assert(util::prefixEditDist("helloo", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("", "hello", 2) == (size_t)0); + assert(util::prefixEditDist("e", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("el", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("ello", "hello", 2) == (size_t)1); + assert(util::prefixEditDist("hell", "hello", 2) == (size_t)0); + assert(util::prefixEditDist("hell", "", 2) > (size_t)2); + assert(util::prefixEditDist("hell", "") == (size_t)4); + } + + // ___________________________________________________________________________ + { + assert(util::toString(34) == "34"); + assert(util::toString("34") == "34"); + } + + // ___________________________________________________________________________ + { + std::string a("lorem ipsum ipsum lorem"); + + assert(util::replace(a, "ips", "aa")); + assert(a == "lorem aaum ipsum lorem"); + + assert(!util::replace(a, "blablabla", "")); + assert(a == "lorem aaum ipsum lorem"); + + assert(util::replace(a, "m", "")); + assert(a == "lore aaum ipsum lorem"); + + assert(!util::replace(a, "", "")); + assert(a == "lore aaum ipsum lorem"); + + std::string b("lorem ipsum ipsum lorem"); + assert(util::replaceAll(b, "ips", "aa")); + assert(b == "lorem aaum aaum lorem"); + + assert(util::replaceAll(b, "m", "")); + assert(b == "lore aau aau lore"); + + assert(util::replaceAll(b, "a", "aa")); + assert(b == "lore aaaau aaaau lore"); + + assert(util::replaceAll(b, "e", "e")); + assert(b == "lore aaaau aaaau lore"); + + assert(util::replaceAll(b, "e", "ee")); + assert(b == "loree aaaau aaaau loree"); + + assert(!util::replaceAll(b, "", "ee")); + assert(b == "loree aaaau aaaau loree"); + } + + // ___________________________________________________________________________ + { + UndirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + g.addEdg(a, c, 1); + g.addEdg(a, b, 5); + g.addEdg(d, c, 1); + g.addEdg(d, b, 3); + g.addEdg(e, d, 1); + g.addEdg(e, b, 1); + + auto comps = util::graph::Algorithm::connectedComponents(g); + + assert(comps.size() == static_cast(1)); + assert(comps[0].count(a)); + assert(comps[0].count(b)); + assert(comps[0].count(c)); + assert(comps[0].count(d)); + assert(comps[0].count(e)); + + auto f = g.addNd("F"); + comps = util::graph::Algorithm::connectedComponents(g); + assert(comps.size() == static_cast(2)); + + auto gn = g.addNd("G"); + comps = util::graph::Algorithm::connectedComponents(g); + assert(comps.size() == static_cast(3)); + + g.addEdg(f, gn, 1); + comps = util::graph::Algorithm::connectedComponents(g); + assert(comps.size() == static_cast(2)); + + g.addEdg(f, a, 1); + comps = util::graph::Algorithm::connectedComponents(g); + assert(comps.size() == static_cast(1)); + } + + // ___________________________________________________________________________ + { + DirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + auto eAC = g.addEdg(a, c, 1); + auto eAB = g.addEdg(a, b, 5); + auto eDC = g.addEdg(d, c, 1); + auto eDB = g.addEdg(d, b, 3); + auto eED = g.addEdg(e, d, 1); + auto eEB = g.addEdg(e, b, 1); + + UNUSED(eAC); + UNUSED(eDC); + UNUSED(eDB); + UNUSED(eED); + UNUSED(eEB); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* from, + const Node* n, + const Edge* to) const { + UNUSED(from); + + // dont count cost of start edge + if (n) return to->pl(); + return 0; + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + auto cost = EDijkstra::shortestPath(eAB, cFunc); + + for (auto u : cost) { + int single = EDijkstra::shortestPath(eAB, u.first, cFunc); + assert(single == u.second); + } + + // all to 1 + auto eBC = g.addEdg(b, c, 10); + + auto costb = EDijkstra::shortestPathRev(eBC, cFunc); + for (auto u : costb) { + int single = EDijkstra::shortestPath(u.first, eBC, cFunc); + assert(single == u.second); + } + } + + // ___________________________________________________________________________ + { + UndirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + auto eAC = g.addEdg(a, c, 1); + auto eAB = g.addEdg(a, b, 5); + auto eDC = g.addEdg(d, c, 1); + auto eDB = g.addEdg(d, b, 3); + auto eED = g.addEdg(e, d, 1); + auto eEB = g.addEdg(e, b, 1); + + UNUSED(eAC); + UNUSED(eDC); + UNUSED(eDB); + UNUSED(eED); + UNUSED(eEB); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* from, + const Node* n, + const Edge* to) const { + UNUSED(from); + + // dont count cost of start edge + if (n) return to->pl(); + return 0; + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + EDijkstra::NList res; + EDijkstra::EList resE; + int cost = EDijkstra::shortestPath(eAB, d, cFunc, &resE, &res); + + assert(cost == 2); + + assert(resE.size() == (size_t)3); + assert(res.size() == (size_t)3); + assert((*(res.rbegin()))->pl() == "A"); + assert((*(++res.rbegin()))->pl() == "C"); + assert((*(++++res.rbegin()))->pl() == "D"); + + assert((*(resE.rbegin())) == eAB); + assert((*(++resE.rbegin())) == eAC); + assert((*(++++resE.rbegin())) == eDC); + + cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res); + assert(cost == 0); + } + + // ___________________________________________________________________________ + { + UndirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + auto eAC = g.addEdg(a, c, 1); + auto eAB = g.addEdg(a, b, 5); + auto eDC = g.addEdg(d, c, 1); + auto eDB = g.addEdg(d, b, 3); + auto eED = g.addEdg(e, d, 1); + auto eEB = g.addEdg(e, b, 1); + + UNUSED(eAC); + UNUSED(eDC); + UNUSED(eDB); + UNUSED(eED); + UNUSED(eEB); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* from, + const Node* n, + const Edge* to) const { + UNUSED(from); + + // dont count cost of start edge + if (n) return to->pl(); + return 0; + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + std::set*> tos; + tos.insert(d); + tos.insert(b); + tos.insert(b); + + EDijkstra::NList res; + EDijkstra::EList resE; + int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res); + assert(cost == 0); + } + + // ___________________________________________________________________________ + { + UndirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + g.addEdg(a, c, 1); + auto eAB = g.addEdg(a, b, 5); + auto eDC = g.addEdg(d, c, 1); + g.addEdg(d, b, 3); + auto eED = g.addEdg(e, d, 1); + g.addEdg(e, b, 1); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* from, + const Node* n, + const Edge* to) const { + UNUSED(from); + + // dont count cost of start edge + if (n) return to->pl(); + return 0; + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + std::set*> tos; + tos.insert(eDC); + tos.insert(eED); + + std::unordered_map*, + EDijkstra::EList*> + resE; + resE[eDC] = new EDijkstra::EList(); + resE[eED] = new EDijkstra::EList(); + std::unordered_map*, + EDijkstra::NList*> + res; + res[eDC] = new EDijkstra::NList(); + res[eED] = new EDijkstra::NList(); + auto hFunc = EDijkstra::ZeroHeurFunc(); + std::unordered_map*, int> cost = + EDijkstra::shortestPath(eAB, tos, cFunc, hFunc, resE, res); + + assert(cost[eDC] == 2); + assert(cost[eED] == 2); + + assert(resE[eDC]->size() == (size_t)3); + assert(res[eED]->size() == (size_t)3); + + assert(resE[eDC]->size() == (size_t)3); + assert(res[eED]->size() == (size_t)3); + } + + // ___________________________________________________________________________ + { + UndirGraph g; + + auto a = g.addNd("A"); + auto b = g.addNd("B"); + auto c = g.addNd("C"); + auto d = g.addNd("D"); + auto e = g.addNd("E"); + + g.addEdg(a, c, 1); + g.addEdg(a, b, 5); + g.addEdg(d, c, 1); + g.addEdg(d, b, 3); + g.addEdg(e, d, 1); + g.addEdg(e, b, 1); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* fr, + const Node* n, + const Edge* to) const { + UNUSED(fr); + UNUSED(n); + return to->pl(); + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + EDijkstra::NList res; + EDijkstra::EList resE; + int cost = EDijkstra::shortestPath(a, b, cFunc, &resE, &res); + + assert(res.size() == (size_t)5); + assert((*(res.rbegin()))->pl() == "A"); + assert((*(++res.rbegin()))->pl() == "C"); + assert((*(++++res.rbegin()))->pl() == "D"); + assert((*(++++++res.rbegin()))->pl() == "E"); + assert((*(++++++++res.rbegin()))->pl() == "B"); + assert(cost == 4); + assert((*(resE.rbegin()))->getFrom()->pl() == "A"); + assert((*(++resE.rbegin()))->getFrom()->pl() == "D"); + assert((*(++++resE.rbegin()))->getFrom()->pl() == "E"); + assert((*(++++++resE.rbegin()))->getTo()->pl() == "B"); + + assert(resE.size() == (size_t)4); + + cost = EDijkstra::shortestPath(d, b, cFunc, &res); + assert(cost == 2); + + cost = EDijkstra::shortestPath(b, d, cFunc, &res); + assert(cost == 2); + + cost = EDijkstra::shortestPath(e, b, cFunc, &res); + assert(cost == 1); + + cost = EDijkstra::shortestPath(b, e, cFunc, &res); + assert(cost == 1); + + cost = EDijkstra::shortestPath(b, a, cFunc, &res); + assert(cost == 4); + + cost = EDijkstra::shortestPath(c, a, cFunc, &res); + assert(cost == 1); + + cost = EDijkstra::shortestPath(a, c, cFunc, &res); + assert(cost == 1); + + cost = EDijkstra::shortestPath(a, d, cFunc, &res); + assert(cost == 2); + } + + // ___________________________________________________________________________ + { + DirGraph g; + + DirNode* a = new DirNode(1); + DirNode* b = new DirNode(4); + g.addNd(a); + g.addNd(b); + + auto c = g.addNd(2); + auto d = g.addNd(3); + auto x = g.addNd(); + + g.addEdg(a, d, 4); + g.addEdg(a, c, 1); + g.addEdg(c, b, 1); + g.addEdg(b, d, 1); + + struct CostFunc : public EDijkstra::CostFunc { + int operator()(const Edge* fr, const Node* n, + const Edge* to) const { + UNUSED(fr); + UNUSED(n); + return to->pl(); + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + EDijkstra::NList res; + int cost = EDijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 3); + + g.addEdg(c, d, 3); + cost = EDijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 3); + + g.addEdg(a, b, 1); + g.addEdg(x, a, 1); + cost = EDijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 2); + } + + // ___________________________________________________________________________ + { + DirGraph g; + + DirNode* a = new DirNode(1); + DirNode* b = new DirNode(0); + g.addNd(a); + g.addNd(b); + + auto c = g.addNd(); + auto d = g.addNd(4); + auto x = g.addNd(); + + g.addEdg(a, d, 4); + g.addEdg(a, c, 1); + g.addEdg(c, b, 1); + g.addEdg(b, d, 1); + + struct CostFunc : public Dijkstra::CostFunc { + int operator()(const Node* fr, const Edge* e, + const Node* to) const { + UNUSED(fr); + UNUSED(to); + return e->pl(); + }; + int inf() const { return 999; }; + }; + + CostFunc cFunc; + + Dijkstra::NList res; + int cost = Dijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 3); + assert(res.size() == (size_t)4); + + g.addEdg(c, d, 3); + cost = Dijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 3); + + g.addEdg(a, b, 1); + g.addEdg(x, a, 1); + cost = Dijkstra::shortestPath(a, d, cFunc, &res); + + assert(cost == 2); + + const std::set*> to{b, c, d, x}; + std::unordered_map*, Dijkstra::EList*> resEdges; + std::unordered_map*, Dijkstra::NList*> resNodes; + + for (auto n : to) { + resEdges[n] = new Dijkstra::EList(); + resNodes[n] = new Dijkstra::NList(); + } + + auto costs = Dijkstra::shortestPath(a, to, cFunc, resEdges, resNodes); + + assert(costs[b] == 1); + assert(costs[c] == 1); + assert(costs[d] == 2); + assert(costs[x] == 999); + } + + // ___________________________________________________________________________ + {{util::Nullable nullable; + assert(nullable.isNull()); +} + +{ + util::Nullable nullable(0); + assert(nullable.isNull()); +} + +{ + std::string str = "aa"; + util::Nullable nullable(&str); + assert(!nullable.isNull()); + + assert(nullable == "aa"); + assert(!(nullable == "aaa")); + assert(!(nullable != "aa")); + assert(nullable == "aa"); + + assert(nullable.get() == "aa"); + assert(std::string(nullable) == "aa"); +} + +{ + int a = 23; + util::Nullable nullable(a); + util::Nullable nullable2(24); + assert(!nullable.isNull()); + + assert(nullable == 23); + assert(nullable >= 23); + assert(nullable <= 23); + assert(nullable < 24); + assert(nullable < 24); + assert(!(nullable < 22)); + assert(nullable != nullable2); + assert(nullable < nullable2); + assert(nullable2 > nullable); + + util::Nullable nullable3(nullable); + assert(nullable == nullable3); + + nullable3 = nullable2; + assert(nullable2 == nullable3); + assert(nullable3 == 24); + assert(nullable2 == 24); + assert(nullable2 == nullable2.get()); + assert(int(nullable2) == nullable2.get()); + assert(!nullable3.isNull()); + assert(!nullable2.isNull()); + + util::Nullable voidnull; + assert(voidnull.isNull()); +} +} // ___________________________________________________________________________ { -CASE("geomwkt") { auto p = pointFromWKT("POINT(10 50)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT( 10 50)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT (10 50 30)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT (10 50 30)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT(10 50 30)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT (10 50) "); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("MPOINT(10 50 30)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("MPOINT(10 50)"); - EXPECT(p.getX() == approx(10)); - EXPECT(p.getY() == approx(50)); + assert(p.getX() == approx(10)); + assert(p.getY() == approx(50)); p = pointFromWKT("POINT(10.05 50.05)"); - EXPECT(p.getX() == approx(10.05)); - EXPECT(p.getY() == approx(50.05)); + assert(p.getX() == approx(10.05)); + assert(p.getY() == approx(50.05)); auto wktl = lineFromWKT("LINESTRING(0 0, 1 1,2 3, 0 1)"); - EXPECT(wktl.size() == (size_t)4); - EXPECT(wktl[0].getX() == approx(0)); - EXPECT(wktl[0].getY() == approx(0)); - EXPECT(wktl[1].getX() == approx(1)); - EXPECT(wktl[1].getY() == approx(1)); - EXPECT(wktl[2].getX() == approx(2)); - EXPECT(wktl[2].getY() == approx(3)); - EXPECT(wktl[3].getX() == approx(0)); - EXPECT(wktl[3].getY() == approx(1)); + assert(wktl.size() == (size_t)4); + assert(wktl[0].getX() == approx(0)); + assert(wktl[0].getY() == approx(0)); + assert(wktl[1].getX() == approx(1)); + assert(wktl[1].getY() == approx(1)); + assert(wktl[2].getX() == approx(2)); + assert(wktl[2].getY() == approx(3)); + assert(wktl[3].getX() == approx(0)); + assert(wktl[3].getY() == approx(1)); wktl = lineFromWKT("MLINESTRING(0 0, 1 1,2 3, 0 1)"); - EXPECT(wktl.size() == (size_t)4); - EXPECT(wktl[0].getX() == approx(0)); - EXPECT(wktl[0].getY() == approx(0)); - EXPECT(wktl[1].getX() == approx(1)); - EXPECT(wktl[1].getY() == approx(1)); - EXPECT(wktl[2].getX() == approx(2)); - EXPECT(wktl[2].getY() == approx(3)); - EXPECT(wktl[3].getX() == approx(0)); - EXPECT(wktl[3].getY() == approx(1)); + assert(wktl.size() == (size_t)4); + assert(wktl[0].getX() == approx(0)); + assert(wktl[0].getY() == approx(0)); + assert(wktl[1].getX() == approx(1)); + assert(wktl[1].getY() == approx(1)); + assert(wktl[2].getX() == approx(2)); + assert(wktl[2].getY() == approx(3)); + assert(wktl[3].getX() == approx(0)); + assert(wktl[3].getY() == approx(1)); wktl = lineFromWKT("MLINESTRING (0 0, 1 1,2 3, 0 1 )"); - EXPECT(wktl.size() == (size_t)4); - EXPECT(wktl[0].getX() == approx(0)); - EXPECT(wktl[0].getY() == approx(0)); - EXPECT(wktl[1].getX() == approx(1)); - EXPECT(wktl[1].getY() == approx(1)); - EXPECT(wktl[2].getX() == approx(2)); - EXPECT(wktl[2].getY() == approx(3)); - EXPECT(wktl[3].getX() == approx(0)); - EXPECT(wktl[3].getY() == approx(1)); -}}, + assert(wktl.size() == (size_t)4); + assert(wktl[0].getX() == approx(0)); + assert(wktl[0].getY() == approx(0)); + assert(wktl[1].getX() == approx(1)); + assert(wktl[1].getY() == approx(1)); + assert(wktl[2].getX() == approx(2)); + assert(wktl[2].getY() == approx(3)); + assert(wktl[3].getX() == approx(0)); + assert(wktl[3].getY() == approx(1)); +} // ___________________________________________________________________________ { -CASE("geometry") { - geo::Point a(1, 2); geo::Point b(2, 3); geo::Point c(4, 5); - EXPECT(a.getX() == approx(1)); - EXPECT(a.getY() == approx(2)); + assert(a.getX() == approx(1)); + assert(a.getY() == approx(2)); a.setX(3); - EXPECT(a.getX() == approx(3)); - EXPECT(a.getY() == approx(2)); + assert(a.getX() == approx(3)); + assert(a.getY() == approx(2)); a.setY(4); - EXPECT(a.getX() == approx(3)); - EXPECT(a.getY() == approx(4)); + assert(a.getX() == approx(3)); + assert(a.getY() == approx(4)); auto d = a + b; - EXPECT(d.getX() == approx(5)); - EXPECT(d.getY() == approx(7)); + assert(d.getX() == approx(5)); + assert(d.getY() == approx(7)); a.setX(1); a.setY(2); - EXPECT(geo::dist(a, a) == approx(0)); - EXPECT(geo::dist(a, b) == approx(sqrt(2))); + assert(geo::dist(a, a) == approx(0)); + assert(geo::dist(a, b) == approx(sqrt(2))); d = d + d; geo::Box box(a, c); - EXPECT(geo::contains(a, box)); - EXPECT(geo::contains(b, box)); - EXPECT(geo::contains(c, box)); - EXPECT(!geo::contains(d, box)); + assert(geo::contains(a, box)); + assert(geo::contains(b, box)); + assert(geo::contains(c, box)); + assert(!geo::contains(d, box)); geo::Line line{a, b, c}; - EXPECT(geo::contains(line, box)); + assert(geo::contains(line, box)); line.push_back(d); - EXPECT(!geo::contains(line, box)); + assert(!geo::contains(line, box)); geo::LineSegment ls{a, b}; - EXPECT(geo::contains(a, ls)); - EXPECT(geo::contains(b, ls)); - EXPECT(!geo::contains(c, ls)); - EXPECT(geo::contains(a + geo::Point(.5, .5), ls)); - EXPECT(!geo::contains(a + geo::Point(1.5, 1.5), ls)); + assert(geo::contains(a, ls)); + assert(geo::contains(b, ls)); + assert(!geo::contains(c, ls)); + assert(geo::contains(a + geo::Point(.5, .5), ls)); + assert(!geo::contains(a + geo::Point(1.5, 1.5), ls)); - geo::LineSegment lsa{geo::Point(1, 1), geo::Point(2, 2)}; - geo::LineSegment lsb{geo::Point(1, 2), geo::Point(2, 1)}; - geo::LineSegment lsc{geo::Point(2.1, 2), geo::Point(3, 3)}; + geo::LineSegment lsa{geo::Point(1, 1), + geo::Point(2, 2)}; + geo::LineSegment lsb{geo::Point(1, 2), + geo::Point(2, 1)}; + geo::LineSegment lsc{geo::Point(2.1, 2), + geo::Point(3, 3)}; - EXPECT(geo::crossProd(lsa.first, lsb) == approx(-1)); - EXPECT(geo::crossProd(lsa.second, lsb) == approx(1)); + assert(geo::crossProd(lsa.first, lsb) == approx(-1)); + assert(geo::crossProd(lsa.second, lsb) == approx(1)); - EXPECT(geo::intersects(lsa, lsb)); + assert(geo::intersects(lsa, lsb)); - EXPECT(!geo::intersects(lsa, lsa)); - EXPECT(!geo::intersects(lsb, lsb)); - EXPECT(!geo::intersects(lsa, lsc)); + assert(!geo::intersects(lsa, lsa)); + assert(!geo::intersects(lsb, lsb)); + assert(!geo::intersects(lsa, lsc)); - EXPECT(!geo::intersects(geo::Point(871569.2, 6104550.4), geo::Point(871581.2, 6104536), geo::Point(871580.3, 6104541.3), geo::Point(871625.7, 6104510.1))); + assert(!geo::intersects(geo::Point(871569.2, 6104550.4), + geo::Point(871581.2, 6104536), + geo::Point(871580.3, 6104541.3), + geo::Point(871625.7, 6104510.1))); - EXPECT(!geo::intersects(geo::Point(0, 0), geo::Point(1, 1), geo::Point(0.5, 0.5), geo::Point(1.5, 1.5))); + assert(!geo::intersects(geo::Point(0, 0), geo::Point(1, 1), + geo::Point(0.5, 0.5), + geo::Point(1.5, 1.5))); - geo::Line l{geo::Point(1, 1), geo::Point(2, 2), geo::Point(2, 4)}; - EXPECT(!geo::contains(geo::Point(1, 2), l)); - EXPECT(geo::contains(geo::Point(2, 2), l)); - EXPECT(geo::contains(geo::Point(2, 3), l)); + geo::Line l{geo::Point(1, 1), geo::Point(2, 2), + geo::Point(2, 4)}; + assert(!geo::contains(geo::Point(1, 2), l)); + assert(geo::contains(geo::Point(2, 2), l)); + assert(geo::contains(geo::Point(2, 3), l)); geo::Box bbox(geo::Point(1, 1), geo::Point(3, 3)); - EXPECT(geo::intersects(l, bbox)); + assert(geo::intersects(l, bbox)); geo::Line ll{geo::Point(0, 0), geo::Point(4, 4)}; - EXPECT(geo::intersects(ll, bbox)); + assert(geo::intersects(ll, bbox)); geo::Line lll{geo::Point(0, 0), geo::Point(0, 4)}; - EXPECT(!geo::intersects(lll, bbox)); + assert(!geo::intersects(lll, bbox)); geo::Line llll{geo::Point(1.2, 0), geo::Point(1, 2)}; - EXPECT(geo::intersects(llll, bbox)); + assert(geo::intersects(llll, bbox)); Line l5new; l5new.push_back(Point(-10, -5)); l5new.push_back(Point(-8, -4)); - EXPECT(geo::getBoundingBox(l5new).getUpperRight().getX() == approx(-8)); - EXPECT(geo::getBoundingBox(l5new).getUpperRight().getY() == approx(-4)); + assert(geo::getBoundingBox(l5new).getUpperRight().getX() == approx(-8)); + assert(geo::getBoundingBox(l5new).getUpperRight().getY() == approx(-4)); Line l5; l5.push_back(Point(0, 0)); l5.push_back(Point(1.5, 2)); Box req(Point(.5, 1), Point(1, 1.5)); - EXPECT(geo::getBoundingBox(l5[0]).getLowerLeft().getX() == approx(0)); - EXPECT(geo::getBoundingBox(l5[0]).getLowerLeft().getY() == approx(0)); + assert(geo::getBoundingBox(l5[0]).getLowerLeft().getX() == approx(0)); + assert(geo::getBoundingBox(l5[0]).getLowerLeft().getY() == approx(0)); - EXPECT(geo::getBoundingBox(l5).getLowerLeft().getX() == approx(0)); - EXPECT(geo::getBoundingBox(l5).getLowerLeft().getY() == approx(0)); - EXPECT(geo::getBoundingBox(l5).getUpperRight().getX() == approx(1.5)); - EXPECT(geo::getBoundingBox(l5).getUpperRight().getY() == approx(2)); - EXPECT(geo::intersects(geo::getBoundingBox(l5), geo::getBoundingBox(Line{Point(.5, 1), Point(1, 1)}))); - EXPECT(geo::intersects(l5, Line{Point(.5, 1), Point(1, 1)})); - EXPECT(geo::intersects(l5, req)); + assert(geo::getBoundingBox(l5).getLowerLeft().getX() == approx(0)); + assert(geo::getBoundingBox(l5).getLowerLeft().getY() == approx(0)); + assert(geo::getBoundingBox(l5).getUpperRight().getX() == approx(1.5)); + assert(geo::getBoundingBox(l5).getUpperRight().getY() == approx(2)); + assert(geo::intersects(geo::getBoundingBox(l5), + geo::getBoundingBox(Line{ + Point(.5, 1), Point(1, 1)}))); + assert(geo::intersects( + l5, Line{Point(.5, 1), Point(1, 1)})); + assert(geo::intersects(l5, req)); Box boxa(Point(1, 1), Point(2, 2)); - EXPECT(geo::intersects(boxa, Box(Point(1.5, 1.5), Point(1.7, 1.7)))); - EXPECT(geo::intersects(boxa, Box(Point(0, 0), Point(3, 3)))); - EXPECT(geo::intersects(boxa, Box(Point(1.5, 1.5), Point(3, 3)))); - EXPECT(geo::intersects(boxa, Box(Point(0, 0), Point(1.5, 1.5)))); + assert(geo::intersects( + boxa, Box(Point(1.5, 1.5), Point(1.7, 1.7)))); + assert(geo::intersects( + boxa, Box(Point(0, 0), Point(3, 3)))); + assert(geo::intersects( + boxa, Box(Point(1.5, 1.5), Point(3, 3)))); + assert(geo::intersects( + boxa, Box(Point(0, 0), Point(1.5, 1.5)))); - EXPECT(geo::intersects(Box(Point(1.5, 1.5), Point(1.7, 1.7)), boxa)); - EXPECT(geo::intersects(Box(Point(0, 0), Point(3, 3)), boxa)); - EXPECT(geo::intersects(Box(Point(1.5, 1.5), Point(3, 3)), boxa)); - EXPECT(geo::intersects(Box(Point(0, 0), Point(1.5, 1.5)), boxa)); + assert(geo::intersects( + Box(Point(1.5, 1.5), Point(1.7, 1.7)), boxa)); + assert(geo::intersects(Box(Point(0, 0), Point(3, 3)), + boxa)); + assert(geo::intersects( + Box(Point(1.5, 1.5), Point(3, 3)), boxa)); + assert(geo::intersects( + Box(Point(0, 0), Point(1.5, 1.5)), boxa)); - Polygon poly({Point(1, 1), Point(3, 2), Point(4, 3), Point(6, 3), Point(5, 1)}); - EXPECT(geo::getWKT(poly) == "POLYGON ((1 1, 3 2, 4 3, 6 3, 5 1, 1 1))"); - EXPECT(geo::contains(Point(4, 2), poly)); - EXPECT(!geo::contains(Point(3, 3), poly)); - EXPECT(geo::contains(Point(1, 1), poly)); - EXPECT(geo::contains(Point(3, 2), poly)); - EXPECT(geo::contains(Point(4, 3), poly)); - EXPECT(geo::contains(Point(6, 3), poly)); - EXPECT(geo::contains(Point(5, 1), poly)); + Polygon poly({Point(1, 1), Point(3, 2), + Point(4, 3), Point(6, 3), + Point(5, 1)}); + assert(geo::getWKT(poly) == "POLYGON ((1 1, 3 2, 4 3, 6 3, 5 1, 1 1))"); + assert(geo::contains(Point(4, 2), poly)); + assert(!geo::contains(Point(3, 3), poly)); + assert(geo::contains(Point(1, 1), poly)); + assert(geo::contains(Point(3, 2), poly)); + assert(geo::contains(Point(4, 3), poly)); + assert(geo::contains(Point(6, 3), poly)); + assert(geo::contains(Point(5, 1), poly)); - EXPECT(geo::contains(Line{Point(6, 3), Point(5, 1)}, poly)); - EXPECT(!geo::contains(Line{Point(6, 3), Point(50, 1)}, poly)); - EXPECT(geo::contains(Line{Point(4, 2), Point(4.5, 2)}, poly)); - EXPECT(geo::contains(Line{Point(4, 2), Point(5, 1)}, poly)); + assert(geo::contains(Line{Point(6, 3), Point(5, 1)}, + poly)); + assert(!geo::contains(Line{Point(6, 3), Point(50, 1)}, + poly)); + assert(geo::contains(Line{Point(4, 2), Point(4.5, 2)}, + poly)); + assert(geo::contains(Line{Point(4, 2), Point(5, 1)}, + poly)); Box polybox(Point(1, 1), Point(6, 4)); - EXPECT(geo::centroid(polybox).getX() == approx(3.5)); - EXPECT(geo::centroid(polybox).getY() == approx(2.5)); - EXPECT(geo::contains(poly, polybox)); - EXPECT(!geo::contains(polybox, poly)); + assert(geo::centroid(polybox).getX() == approx(3.5)); + assert(geo::centroid(polybox).getY() == approx(2.5)); + assert(geo::contains(poly, polybox)); + assert(!geo::contains(polybox, poly)); Box polybox2(Point(4, 1), Point(5, 2)); - EXPECT(geo::contains(polybox2, poly)); - EXPECT(geo::contains(poly, getBoundingBox(poly))); + assert(geo::contains(polybox2, poly)); + assert(geo::contains(poly, getBoundingBox(poly))); Point rotP(2, 2); - EXPECT(geo::dist(geo::rotate(rotP, 180, Point(1, 1)), Point(0, 0)) == approx(0)); - EXPECT(geo::dist(geo::rotate(rotP, 360, Point(1, 1)), rotP) == approx(0)); + assert(geo::dist(geo::rotate(rotP, 180, Point(1, 1)), + Point(0, 0)) == approx(0)); + assert(geo::dist(geo::rotate(rotP, 360, Point(1, 1)), rotP) == + approx(0)); Line rotLine({{1, 1}, {3, 3}}); - EXPECT(geo::rotate(rotLine, 90, Point(2, 2))[0].getX() == approx(1)); - EXPECT(geo::rotate(rotLine, 90, Point(2, 2))[0].getY() == approx(3)); - EXPECT(geo::rotate(rotLine, 90, Point(2, 2))[1].getX() == approx(3)); - EXPECT(geo::rotate(rotLine, 90, Point(2, 2))[1].getY() == approx(1)); + assert(geo::rotate(rotLine, 90, Point(2, 2))[0].getX() == approx(1)); + assert(geo::rotate(rotLine, 90, Point(2, 2))[0].getY() == approx(3)); + assert(geo::rotate(rotLine, 90, Point(2, 2))[1].getX() == approx(3)); + assert(geo::rotate(rotLine, 90, Point(2, 2))[1].getY() == approx(1)); MultiLine multiRotLine({{{1, 1}, {3, 3}}, {{1, 3}, {3, 1}}}); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[0][0].getX() == approx(1)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[0][0].getY() == approx(3)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[0][1].getX() == approx(3)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[0][1].getY() == approx(1)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[1][0].getX() == approx(3)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[1][0].getY() == approx(3)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[1][1].getX() == approx(1)); - EXPECT(geo::rotate(multiRotLine, 90, Point(2, 2))[1][1].getY() == approx(1)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[0][0].getX() == + approx(1)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[0][0].getY() == + approx(3)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[0][1].getX() == + approx(3)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[0][1].getY() == + approx(1)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[1][0].getX() == + approx(3)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[1][0].getY() == + approx(3)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[1][1].getX() == + approx(1)); + assert(geo::rotate(multiRotLine, 90, Point(2, 2))[1][1].getY() == + approx(1)); - EXPECT(geo::getWKT(multiRotLine) == "MULTILINESTRING ((1 1, 3 3), (1 3, 3 1))"); + assert(geo::getWKT(multiRotLine) == + "MULTILINESTRING ((1 1, 3 3), (1 3, 3 1))"); - EXPECT(geo::contains(multiRotLine[0], geo::move(geo::move(multiRotLine, 1.0, 2.0), -1.0, -2.0)[0])); - EXPECT(geo::contains(multiRotLine, geo::getBoundingBox(Line{{1, 1}, {3, 3}, {1, 3}, {3, 1}}))); + assert(geo::contains( + multiRotLine[0], + geo::move(geo::move(multiRotLine, 1.0, 2.0), -1.0, -2.0)[0])); + assert(geo::contains(multiRotLine, geo::getBoundingBox(Line{ + {1, 1}, {3, 3}, {1, 3}, {3, 1}}))); - EXPECT(geo::contains(getBoundingBox(multiRotLine), geo::getBoundingBox(Line{{1, 1}, {3, 3}, {1, 3}, {3, 1}}))); - EXPECT(geo::contains(geo::getBoundingBox(Line{{1, 1}, {3, 3}, {1, 3}, {3, 1}}), getBoundingBox(multiRotLine))); + assert(geo::contains( + getBoundingBox(multiRotLine), + geo::getBoundingBox(Line{{1, 1}, {3, 3}, {1, 3}, {3, 1}}))); + assert(geo::contains( + geo::getBoundingBox(Line{{1, 1}, {3, 3}, {1, 3}, {3, 1}}), + getBoundingBox(multiRotLine))); - EXPECT(geo::dist(geo::centroid(rotP), rotP) == approx(0)); - EXPECT(geo::dist(geo::centroid(rotLine), rotP) == approx(0)); - EXPECT(geo::dist(geo::centroid(polybox), Point(3.5, 2.5)) == approx(0)); - EXPECT(geo::dist(geo::centroid(Polygon({{0, 0}, {3, 4}, {4,3}})), Point(7.0/3.0,7.0/3.0)) == approx(0)); + assert(geo::dist(geo::centroid(rotP), rotP) == approx(0)); + assert(geo::dist(geo::centroid(rotLine), rotP) == approx(0)); + assert(geo::dist(geo::centroid(polybox), Point(3.5, 2.5)) == + approx(0)); + assert(geo::dist(geo::centroid(Polygon({{0, 0}, {3, 4}, {4, 3}})), + Point(7.0 / 3.0, 7.0 / 3.0)) == approx(0)); - auto polyy = Polygon({{0, 0}, {3, 4}, {4,3}}); + auto polyy = Polygon({{0, 0}, {3, 4}, {4, 3}}); MultiPolygon mpoly{polyy, polyy}; - EXPECT(geo::getWKT(polyy) == "POLYGON ((0 0, 3 4, 4 3, 0 0))"); - EXPECT(geo::getWKT(mpoly) == "MULTIPOLYGON (((0 0, 3 4, 4 3, 0 0)), ((0 0, 3 4, 4 3, 0 0)))"); + assert(geo::getWKT(polyy) == "POLYGON ((0 0, 3 4, 4 3, 0 0))"); + assert(geo::getWKT(mpoly) == + "MULTIPOLYGON (((0 0, 3 4, 4 3, 0 0)), ((0 0, 3 4, 4 3, 0 0)))"); - auto hull = geo::convexHull(Line{{0.1, 3}, {1, 1}, {2, 2}, {4, 4}, {0, 0}, {1, 2}, {3, 1}, {3, 3}}); - EXPECT(hull.getOuter().size() == size_t(4)); - EXPECT(hull.getOuter()[0].getX() == approx(0)); - EXPECT(hull.getOuter()[0].getY() == approx(0)); - EXPECT(hull.getOuter()[1].getX() == approx(3)); - EXPECT(hull.getOuter()[1].getY() == approx(1)); - EXPECT(hull.getOuter()[2].getX() == approx(4)); - EXPECT(hull.getOuter()[2].getY() == approx(4)); - EXPECT(hull.getOuter()[3].getX() == approx(0.1)); - EXPECT(hull.getOuter()[3].getY() == approx(3)); - EXPECT(geo::contains(geo::convexHull(geo::getBoundingBox(poly)), geo::getBoundingBox(poly))); - EXPECT(geo::contains(geo::getBoundingBox(poly), geo::convexHull(geo::getBoundingBox(poly)))); + auto hull = geo::convexHull(Line{ + {0.1, 3}, {1, 1}, {2, 2}, {4, 4}, {0, 0}, {1, 2}, {3, 1}, {3, 3}}); + assert(hull.getOuter().size() == size_t(4)); + assert(hull.getOuter()[0].getX() == approx(0)); + assert(hull.getOuter()[0].getY() == approx(0)); + assert(hull.getOuter()[1].getX() == approx(0.1)); + assert(hull.getOuter()[1].getY() == approx(3)); + assert(hull.getOuter()[2].getX() == approx(4)); + assert(hull.getOuter()[2].getY() == approx(4)); + assert(hull.getOuter()[3].getX() == approx(3)); + assert(hull.getOuter()[3].getY() == approx(1)); + assert(geo::contains(geo::convexHull(geo::getBoundingBox(poly)), + geo::getBoundingBox(poly))); + assert(geo::contains(geo::getBoundingBox(poly), + geo::convexHull(geo::getBoundingBox(poly)))); - auto hull2 = geo::convexHull(Line{{0.1, 3}, {1, 1}, {2, 2}, {4, 4}, {0, 0}, {1, 2}, {3, 1}, {3, 3}, {-0.1, 1}}); - EXPECT(hull2.getOuter().size() == size_t(5)); - EXPECT(hull2.getOuter()[0].getX() == approx(-.1)); - EXPECT(hull2.getOuter()[0].getY() == approx(1)); - EXPECT(hull2.getOuter()[1].getX() == approx(0)); - EXPECT(hull2.getOuter()[1].getY() == approx(0)); - EXPECT(hull2.getOuter()[2].getX() == approx(3)); - EXPECT(hull2.getOuter()[2].getY() == approx(1)); - EXPECT(hull2.getOuter()[3].getX() == approx(4)); - EXPECT(hull2.getOuter()[3].getY() == approx(4)); - EXPECT(hull2.getOuter()[4].getX() == approx(0.1)); - EXPECT(hull2.getOuter()[4].getY() == approx(3)); + auto hull2 = geo::convexHull(Line{{0.1, 3}, + {1, 1}, + {2, 2}, + {4, 4}, + {0, 0}, + {1, 2}, + {3, 1}, + {3, 3}, + {-0.1, 1}}); + assert(hull2.getOuter().size() == size_t(5)); + assert(hull2.getOuter()[0].getX() == approx(-.1)); + assert(hull2.getOuter()[0].getY() == approx(1)); + assert(hull2.getOuter()[1].getX() == approx(0.1)); + assert(hull2.getOuter()[1].getY() == approx(3)); + assert(hull2.getOuter()[2].getX() == approx(4)); + assert(hull2.getOuter()[2].getY() == approx(4)); + assert(hull2.getOuter()[3].getX() == approx(3)); + assert(hull2.getOuter()[3].getY() == approx(1)); + assert(hull2.getOuter()[4].getX() == approx(0)); + assert(hull2.getOuter()[4].getY() == approx(0)); - auto hull3 = geo::convexHull(Line{{0.1, 3}, {4, 4}, {0, 0}, {1, 2}, {3, 1}}); - EXPECT(hull3.getOuter().size() == size_t(4)); - EXPECT(hull3.getOuter()[0].getX() == approx(0)); - EXPECT(hull3.getOuter()[0].getY() == approx(0)); - EXPECT(hull3.getOuter()[1].getX() == approx(3)); - EXPECT(hull3.getOuter()[1].getY() == approx(1)); - EXPECT(hull3.getOuter()[2].getX() == approx(4)); - EXPECT(hull3.getOuter()[2].getY() == approx(4)); - EXPECT(hull3.getOuter()[3].getX() == approx(0.1)); - EXPECT(hull3.getOuter()[3].getY() == approx(3)); + auto hull3 = + geo::convexHull(Line{{0.1, 3}, {4, 4}, {0, 0}, {1, 2}, {3, 1}}); + assert(hull3.getOuter().size() == size_t(4)); + assert(hull3.getOuter()[0].getX() == approx(0)); + assert(hull3.getOuter()[0].getY() == approx(0)); + assert(hull3.getOuter()[3].getX() == approx(3)); + assert(hull3.getOuter()[3].getY() == approx(1)); + assert(hull3.getOuter()[2].getX() == approx(4)); + assert(hull3.getOuter()[2].getY() == approx(4)); + assert(hull3.getOuter()[1].getX() == approx(0.1)); + assert(hull3.getOuter()[1].getY() == approx(3)); - hull3 = geo::convexHull(Line{{0.1, 3}, {4, 4}, {2, 1}, {3, 2}, {0, 0}, {1, 2}, {3, 1}}); - EXPECT(hull3.getOuter().size() == size_t(4)); - EXPECT(hull3.getOuter()[0].getX() == approx(0)); - EXPECT(hull3.getOuter()[0].getY() == approx(0)); - EXPECT(hull3.getOuter()[1].getX() == approx(3)); - EXPECT(hull3.getOuter()[1].getY() == approx(1)); - EXPECT(hull3.getOuter()[2].getX() == approx(4)); - EXPECT(hull3.getOuter()[2].getY() == approx(4)); - EXPECT(hull3.getOuter()[3].getX() == approx(0.1)); - EXPECT(hull3.getOuter()[3].getY() == approx(3)); + hull3 = geo::convexHull( + Line{{0.1, 3}, {4, 4}, {2, 1}, {3, 2}, {0, 0}, {1, 2}, {3, 1}}); + assert(hull3.getOuter().size() == size_t(4)); + assert(hull3.getOuter()[0].getX() == approx(0)); + assert(hull3.getOuter()[0].getY() == approx(0)); + assert(hull3.getOuter()[3].getX() == approx(3)); + assert(hull3.getOuter()[3].getY() == approx(1)); + assert(hull3.getOuter()[2].getX() == approx(4)); + assert(hull3.getOuter()[2].getY() == approx(4)); + assert(hull3.getOuter()[1].getX() == approx(0.1)); + assert(hull3.getOuter()[1].getY() == approx(3)); - hull3 = geo::convexHull(Line{{4, 4}, {1, 2}, {2, 1}, {3, 2}, {0.1, 3}, {0, 0}, {1, 2}, {3, 1}}); - EXPECT(hull3.getOuter().size() == size_t(4)); - EXPECT(hull3.getOuter()[0].getX() == approx(0)); - EXPECT(hull3.getOuter()[0].getY() == approx(0)); - EXPECT(hull3.getOuter()[1].getX() == approx(3)); - EXPECT(hull3.getOuter()[1].getY() == approx(1)); - EXPECT(hull3.getOuter()[2].getX() == approx(4)); - EXPECT(hull3.getOuter()[2].getY() == approx(4)); - EXPECT(hull3.getOuter()[3].getX() == approx(0.1)); - EXPECT(hull3.getOuter()[3].getY() == approx(3)); + hull3 = geo::convexHull(Line{ + {4, 4}, {1, 2}, {2, 1}, {3, 2}, {0.1, 3}, {0, 0}, {1, 2}, {3, 1}}); + assert(hull3.getOuter().size() == size_t(4)); + assert(hull3.getOuter()[0].getX() == approx(0)); + assert(hull3.getOuter()[0].getY() == approx(0)); + assert(hull3.getOuter()[3].getX() == approx(3)); + assert(hull3.getOuter()[3].getY() == approx(1)); + assert(hull3.getOuter()[2].getX() == approx(4)); + assert(hull3.getOuter()[2].getY() == approx(4)); + assert(hull3.getOuter()[1].getX() == approx(0.1)); + assert(hull3.getOuter()[1].getY() == approx(3)); hull3 = geo::convexHull(Line{{4, 4}, {1, 2}, {3, 1}}); - EXPECT(hull3.getOuter().size() == size_t(3)); - EXPECT(hull3.getOuter()[0].getX() == approx(1)); - EXPECT(hull3.getOuter()[0].getY() == approx(2)); - EXPECT(hull3.getOuter()[1].getX() == approx(3)); - EXPECT(hull3.getOuter()[1].getY() == approx(1)); - EXPECT(hull3.getOuter()[2].getX() == approx(4)); - EXPECT(hull3.getOuter()[2].getY() == approx(4)); + assert(hull3.getOuter().size() == size_t(3)); + assert(hull3.getOuter()[0].getX() == approx(1)); + assert(hull3.getOuter()[0].getY() == approx(2)); + assert(hull3.getOuter()[2].getX() == approx(3)); + assert(hull3.getOuter()[2].getY() == approx(1)); + assert(hull3.getOuter()[1].getX() == approx(4)); + assert(hull3.getOuter()[1].getY() == approx(4)); hull3 = geo::convexHull(Line{{4, 4}, {1, 2}, {3, 10}}); - EXPECT(hull3.getOuter().size() == size_t(3)); - EXPECT(hull3.getOuter()[0].getX() == approx(1)); - EXPECT(hull3.getOuter()[0].getY() == approx(2)); - EXPECT(hull3.getOuter()[1].getX() == approx(4)); - EXPECT(hull3.getOuter()[1].getY() == approx(4)); - EXPECT(hull3.getOuter()[2].getX() == approx(3)); - EXPECT(hull3.getOuter()[2].getY() == approx(10)); + assert(hull3.getOuter().size() == size_t(3)); + assert(hull3.getOuter()[0].getX() == approx(1)); + assert(hull3.getOuter()[0].getY() == approx(2)); + assert(hull3.getOuter()[2].getX() == approx(4)); + assert(hull3.getOuter()[2].getY() == approx(4)); + assert(hull3.getOuter()[1].getX() == approx(3)); + assert(hull3.getOuter()[1].getY() == approx(10)); Line test{{0.3215348546593775, 0.03629583077160248}, {0.02402358131857918, -0.2356728797179394}, @@ -1369,88 +1441,269 @@ CASE("geometry") { }; hull3 = geo::convexHull(test); - EXPECT(geo::contains(test, hull3)); - EXPECT(hull3.getOuter().size() == size_t(8)); - EXPECT(geo::contains(Polygon({{-0.161920957418085, -0.4055339716426413}, - {0.05054295812784038, 0.4754929463150845}, - {0.4823896228171788, -0.4776170002088109}, - {0.4932166845474547, 0.4928094162538735}, - {-0.3521487911717489, 0.4352656197131292}, - {-0.4907368011686362, 0.1865826865533206}, - {0.4916198379282093, -0.345391701297268}, - {-0.4404289572876217, - -0.2894855991839297}}), hull3)); - EXPECT(geo::contains(hull3, Polygon({{-0.161920957418085, -0.4055339716426413}, - {0.05054295812784038, 0.4754929463150845}, - {0.4823896228171788, -0.4776170002088109}, - {0.4932166845474547, 0.4928094162538735}, - {-0.3521487911717489, 0.4352656197131292}, - {-0.4907368011686362, 0.1865826865533206}, - {0.4916198379282093, -0.345391701297268}, - {-0.4404289572876217, - -0.2894855991839297}}))); + assert(geo::contains(test, hull3)); + assert(hull3.getOuter().size() == size_t(8)); + assert(geo::contains( + Polygon({{-0.161920957418085, -0.4055339716426413}, + {0.05054295812784038, 0.4754929463150845}, + {0.4823896228171788, -0.4776170002088109}, + {0.4932166845474547, 0.4928094162538735}, + {-0.3521487911717489, 0.4352656197131292}, + {-0.4907368011686362, 0.1865826865533206}, + {0.4916198379282093, -0.345391701297268}, + {-0.4404289572876217, -0.2894855991839297}}), + hull3)); + assert(geo::contains( + hull3, Polygon({{-0.161920957418085, -0.4055339716426413}, + {0.05054295812784038, 0.4754929463150845}, + {0.4823896228171788, -0.4776170002088109}, + {0.4932166845474547, 0.4928094162538735}, + {-0.3521487911717489, 0.4352656197131292}, + {-0.4907368011686362, 0.1865826865533206}, + {0.4916198379282093, -0.345391701297268}, + {-0.4404289572876217, -0.2894855991839297}}))); - hull3 = geo::convexHull(Line{{3, 6}, {8, 10}, {3, 5}, {20, -10}, {-4, 5}, {10, 2}, {5, 1}, {45, 1}, {30, -9}, {3, 14}, {25, -5.5}}); - EXPECT(hull3.getOuter().size() == size_t(5)); - EXPECT(hull3.getOuter()[0].getX() == approx(-4)); - EXPECT(hull3.getOuter()[0].getY() == approx(5)); - EXPECT(hull3.getOuter()[1].getX() == approx(20)); - EXPECT(hull3.getOuter()[1].getY() == approx(-10)); - EXPECT(hull3.getOuter()[2].getX() == approx(30)); - EXPECT(hull3.getOuter()[2].getY() == approx(-9)); - EXPECT(hull3.getOuter()[3].getX() == approx(45)); - EXPECT(hull3.getOuter()[3].getY() == approx(1)); - EXPECT(hull3.getOuter()[4].getX() == approx(3)); - EXPECT(hull3.getOuter()[4].getY() == approx(14)); + hull3 = geo::convexHull(Line{{3, 6}, + {8, 10}, + {3, 5}, + {20, -10}, + {-4, 5}, + {10, 2}, + {5, 1}, + {45, 1}, + {30, -9}, + {3, 14}, + {25, -5.5}}); + assert(hull3.getOuter().size() == size_t(5)); + assert(hull3.getOuter()[0].getX() == approx(-4)); + assert(hull3.getOuter()[0].getY() == approx(5)); + assert(hull3.getOuter()[4].getX() == approx(20)); + assert(hull3.getOuter()[4].getY() == approx(-10)); + assert(hull3.getOuter()[3].getX() == approx(30)); + assert(hull3.getOuter()[3].getY() == approx(-9)); + assert(hull3.getOuter()[2].getX() == approx(45)); + assert(hull3.getOuter()[2].getY() == approx(1)); + assert(hull3.getOuter()[1].getX() == approx(3)); + assert(hull3.getOuter()[1].getY() == approx(14)); - hull3 = geo::convexHull(Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}); - EXPECT(hull3.getOuter().size() == size_t(8)); - EXPECT(geo::contains(geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}), hull3)); - EXPECT(geo::contains(hull3, geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}))); + hull3 = geo::convexHull(Line{ + {7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}); + assert(hull3.getOuter().size() == size_t(8)); + assert(geo::contains(geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}), + hull3)); + assert(geo::contains(hull3, geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}))); - hull3 = geo::convexHull(Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}, {0, 0}, {1, 2}, {-2, 1}, {-1, -1}, {3, 4}, {4, 3}, {-5, 4}, {6, 5}}); - EXPECT(hull3.getOuter().size() == size_t(8)); - EXPECT(geo::contains(geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}), hull3)); - EXPECT(geo::contains(hull3, geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}))); + hull3 = geo::convexHull(Line{{7, 7}, + {7, -7}, + {-7, -7}, + {-7, 7}, + {9, 0}, + {-9, 0}, + {0, 9}, + {0, -9}, + {0, 0}, + {1, 2}, + {-2, 1}, + {-1, -1}, + {3, 4}, + {4, 3}, + {-5, 4}, + {6, 5}}); + assert(hull3.getOuter().size() == size_t(8)); + assert(geo::contains(geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}), + hull3)); + assert(geo::contains(hull3, geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}))); - hull3 = geo::convexHull(Line{{0, 0}, {1, 2}, {-2, 1}, {-1, -1}, {3, 4}, {4, 3}, {-5, 4}, {6, 5}, {7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}, {-8, 0}, {8, 0}, {-7, 0}, {7, 0}, {-6, 0}, {6, 0}, {-5, 0}, {5, 0}, {-4, 0}, {4, 0}, {-3, 0}, {3, 0}, {-2, 0}, {2, 0}, {-1, 0}, {1, 0}, {0, -8}, {0, 8}, {0, -7}, {0, 7}, {0, -6}, {0, 6}, {0, -5}, {0, 5}, {0, -4}, {0, 4}, {0, -3}, {0, 3}, {0, -2}, {0, 2}, {0, -1}, {0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}, {-1, 1}, {-2, 2}, {-3, 3}, {-4, 4}, {-5, 5}, {-6, 6}, {-1, -1}, {-2, -2}, {-3, -3}, {-4, -4}, {-5, -5}, {-6, -6}}); - EXPECT(hull3.getOuter().size() == size_t(8)); - EXPECT(geo::contains(geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}), hull3)); - EXPECT(geo::contains(hull3, geo::Polygon({{-9, 0}, {-7, -7}, {0, -9}, {7, -7}, {9, 0}, {7, 7}, {0, 9}, {-7, 7}}))); + hull3 = geo::convexHull(Line{ + {0, 0}, {1, 2}, {-2, 1}, {-1, -1}, {3, 4}, {4, 3}, {-5, 4}, + {6, 5}, {7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, + {0, 9}, {0, -9}, {-8, 0}, {8, 0}, {-7, 0}, {7, 0}, {-6, 0}, + {6, 0}, {-5, 0}, {5, 0}, {-4, 0}, {4, 0}, {-3, 0}, {3, 0}, + {-2, 0}, {2, 0}, {-1, 0}, {1, 0}, {0, -8}, {0, 8}, {0, -7}, + {0, 7}, {0, -6}, {0, 6}, {0, -5}, {0, 5}, {0, -4}, {0, 4}, + {0, -3}, {0, 3}, {0, -2}, {0, 2}, {0, -1}, {0, 1}, {1, 1}, + {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {1, -1}, {2, -2}, + {3, -3}, {4, -4}, {5, -5}, {6, -6}, {-1, 1}, {-2, 2}, {-3, 3}, + {-4, 4}, {-5, 5}, {-6, 6}, {-1, -1}, {-2, -2}, {-3, -3}, {-4, -4}, + {-5, -5}, {-6, -6}}); + assert(hull3.getOuter().size() == size_t(8)); + assert(geo::contains(geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}), + hull3)); + assert(geo::contains(hull3, geo::Polygon({{-9, 0}, + {-7, -7}, + {0, -9}, + {7, -7}, + {9, 0}, + {7, 7}, + {0, 9}, + {-7, 7}}))); - EXPECT(geo::area(geo::Point(1, 2)) == approx(0)); - EXPECT(geo::area(geo::Line{{1, 2}, {2, 5}}) == approx(0)); - EXPECT(geo::area(geo::Box({0, 0}, {1, 1})) == approx(1)); - EXPECT(geo::area(geo::Box({1, 1}, {1, 1})) == approx(0)); - EXPECT(geo::area(geo::Box({0, 0}, {2, 2})) == approx(4)); - EXPECT(geo::area(geo::Polygon({{0, 0}, {1, 0}, {1, 1}, {0, 1}})) == approx(1)); - EXPECT(geo::area(geo::Polygon({{0, 0}, {1, 0}, {1, 1}})) == approx(0.5)); + assert(geo::area(geo::Point(1, 2)) == approx(0)); + assert(geo::area(geo::Line{{1, 2}, {2, 5}}) == approx(0)); + assert(geo::area(geo::Box({0, 0}, {1, 1})) == approx(1)); + assert(geo::area(geo::Box({1, 1}, {1, 1})) == approx(0)); + assert(geo::area(geo::Box({0, 0}, {2, 2})) == approx(4)); + assert(geo::area(geo::Polygon({{0, 0}, {1, 0}, {1, 1}, {0, 1}})) == + approx(1)); + assert(geo::area(geo::Polygon({{0, 0}, {1, 0}, {1, 1}})) == + approx(0.5)); - auto obox = geo::getOrientedEnvelope(geo::Line{{0, 0}, {1, 1}, {1.5, 0.5}}); - EXPECT(geo::contains(geo::convexHull(obox), geo::Polygon({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}))); - EXPECT(geo::contains(geo::Polygon({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}), geo::convexHull(obox))); + auto obox = + geo::getOrientedEnvelope(geo::Line{{0, 0}, {1, 1}, {1.5, 0.5}}); + assert(geo::contains( + geo::convexHull(obox), + geo::Polygon({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}))); + assert(geo::contains( + geo::Polygon({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}), + geo::convexHull(obox))); - EXPECT(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, geo::LineSegment{{2, 2}, {2, 0}}) == approx(0)); - EXPECT(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, geo::LineSegment{{2, 4}, {2, 2}}) == approx(1)); - EXPECT(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, geo::LineSegment{{1, 1}, {3, 1}}) == approx(0)); - EXPECT(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, geo::LineSegment{{1, 2}, {3, 2}}) == approx(1)); - EXPECT(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, geo::LineSegment{{1, 2}, {3, 5}}) == approx(1)); + assert(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, + geo::LineSegment{{2, 2}, {2, 0}}) == approx(0)); + assert(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, + geo::LineSegment{{2, 4}, {2, 2}}) == approx(1)); + assert(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, + geo::LineSegment{{1, 1}, {3, 1}}) == approx(0)); + assert(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, + geo::LineSegment{{1, 2}, {3, 2}}) == approx(1)); + assert(geo::dist(geo::LineSegment{{1, 1}, {3, 1}}, + geo::LineSegment{{1, 2}, {3, 5}}) == approx(1)); - EXPECT(geo::dist(geo::Line{{1, 1}, {3, 1}}, geo::Point{2, 1}) == approx(0)); - EXPECT(geo::dist(geo::Line{{1, 1}, {3, 1}}, geo::Point{2, 2}) == approx(1)); - EXPECT(geo::dist(geo::Line{{1, 1}, {3, 1}}, geo::Point{3, 1}) == approx(0)); - EXPECT(geo::dist(geo::Line{{1, 1}, {3, 1}}, geo::Point{1, 1}) == approx(0)); + assert(geo::dist(geo::Line{{1, 1}, {3, 1}}, + geo::Point{2, 1}) == approx(0)); + assert(geo::dist(geo::Line{{1, 1}, {3, 1}}, + geo::Point{2, 2}) == approx(1)); + assert(geo::dist(geo::Line{{1, 1}, {3, 1}}, + geo::Point{3, 1}) == approx(0)); + assert(geo::dist(geo::Line{{1, 1}, {3, 1}}, + geo::Point{1, 1}) == approx(0)); - EXPECT(geo::dist(Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}, Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}) == approx(0)); - EXPECT(geo::dist(Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}, LineSegment{{6, 7}, {8, -7}}) == approx(0)); - EXPECT(geo::dist(Line{{7, 7}, {7, -7}, {-7, -7}, {-7, 7}, {9, 0}, {-9, 0}, {0, 9}, {0, -9}}, Point{7, 4}) == approx(0)); - EXPECT(geo::dist(Line{{0, 0}, {1, 1}, {2, 0}}, Line{{1.5, 0.5}, {1.5, 100}}) == approx(0)); - EXPECT(geo::dist(Line{{0, 0}, {1, 1}, {2, 0}}, Line{{2, 0.5}, {2, 100}}) == approx(0.353553)); + assert(geo::dist(Line{{7, 7}, + {7, -7}, + {-7, -7}, + {-7, 7}, + {9, 0}, + {-9, 0}, + {0, 9}, + {0, -9}}, + Line{{7, 7}, + {7, -7}, + {-7, -7}, + {-7, 7}, + {9, 0}, + {-9, 0}, + {0, 9}, + {0, -9}}) == approx(0)); + assert(geo::dist(Line{{7, 7}, + {7, -7}, + {-7, -7}, + {-7, 7}, + {9, 0}, + {-9, 0}, + {0, 9}, + {0, -9}}, + LineSegment{{6, 7}, {8, -7}}) == approx(0)); + assert(geo::dist(Line{{7, 7}, + {7, -7}, + {-7, -7}, + {-7, 7}, + {9, 0}, + {-9, 0}, + {0, 9}, + {0, -9}}, + Point{7, 4}) == approx(0)); + assert(geo::dist(Line{{0, 0}, {1, 1}, {2, 0}}, + Line{{1.5, 0.5}, {1.5, 100}}) == approx(0)); + assert(geo::dist(Line{{0, 0}, {1, 1}, {2, 0}}, + Line{{2, 0.5}, {2, 100}}) == approx(0.353553)); + + assert(geo::contains(util::geo::Point{1.5, 0.5}, + util::geo::LineSegment{{1, 1}, {1.5, 0.5}})); + assert(geo::contains(util::geo::Point{1.5, 0.5}, + util::geo::LineSegment{{1, 1}, {1.5, 0.5}})); + + auto polyTest = + geo::Polygon({{1, 1}, {3, 1}, {2, 2}, {3, 3}, {1, 3}}); + assert(!geo::contains(util::geo::LineSegment({2.5, 1.3}, {2.5, 2.6}), + polyTest)); + + assert(!geo::contains(util::geo::LineSegment{{2.5, 1.3}, {2.5, 2.6}}, + polyTest)); + assert(geo::contains(util::geo::LineSegment{{2.5, 2.6}, {1.5, 2}}, + polyTest)); + assert(!geo::contains( + util::geo::Line{{2.5, 1.3}, {2.5, 2.6}, {1.5, 2}}, polyTest)); + assert(geo::contains( + util::geo::Line{{2.5, 1.3}, {1.5, 2}, {2.5, 2.6}}, polyTest)); + + assert(!geo::contains(util::geo::Box{{1, 1}, {2.5, 2.6}}, polyTest)); + + assert( + geo::intersects(Box(Point(0, 0), Point(10, 10)), + Box(Point(2, 2), Point(8, 8)))); + assert( + geo::intersects(Box(Point(0, 0), Point(10, 10)), + Box(Point(-2, -2), Point(8, 8)))); + assert(geo::intersects( + Box(Point(0, 0), Point(10, 10)), + Box(Point(-2, -2), Point(12, 12)))); + assert( + geo::intersects(Box(Point(0, 0), Point(10, 10)), + Box(Point(5, 5), Point(12, 12)))); + + assert(!geo::intersects( + Box(Point(0, 0), Point(10, 10)), + Box(Point(15, 15), Point(12, 12)))); + + double rad = 10.0; + int n = 20; + util::geo::MultiPoint mp; + + for (int i = 0; i < n; i++) { + double x = rad * cos((2.0 * M_PI / static_cast(n)) * + static_cast(i)); + double y = rad * sin((2.0 * M_PI / static_cast(n)) * + static_cast(i)); + + mp.push_back(util::geo::DPoint(x, y)); + } + + auto h = util::geo::convexHull(mp); + + assert(geo::contains(mp, h)); } - -}}; - -// _____________________________________________________________________________ -int main(int argc, char** argv) { - return(lest::run(specification, argc, argv)); } diff --git a/src/util/tests/lest.h b/src/util/tests/lest.h index 71db282..f3c9f49 100644 --- a/src/util/tests/lest.h +++ b/src/util/tests/lest.h @@ -31,7 +31,11 @@ #include #include -#define lest_VERSION "1.33.1" +#define lest_MAJOR 1 +#define lest_MINOR 35 +#define lest_PATCH 1 + +#define lest_VERSION lest_STRINGIFY(lest_MAJOR) "." lest_STRINGIFY(lest_MINOR) "." lest_STRINGIFY(lest_PATCH) #ifndef lest_FEATURE_AUTO_REGISTER # define lest_FEATURE_AUTO_REGISTER 0 @@ -49,50 +53,55 @@ # define lest_FEATURE_REGEX_SEARCH 0 #endif -#ifndef lest_FEATURE_TIME_PRECISION -#define lest_FEATURE_TIME_PRECISION 0 +#ifndef lest_FEATURE_TIME_PRECISION +# define lest_FEATURE_TIME_PRECISION 0 #endif -#ifndef lest_FEATURE_WSTRING -#define lest_FEATURE_WSTRING 1 +#ifndef lest_FEATURE_WSTRING +# define lest_FEATURE_WSTRING 1 #endif -#ifdef lest_FEATURE_RTTI -# define lest__cpp_rtti lest_FEATURE_RTTI +#ifdef lest_FEATURE_RTTI +# define lest__cpp_rtti lest_FEATURE_RTTI #elif defined(__cpp_rtti) -# define lest__cpp_rtti __cpp_rtti +# define lest__cpp_rtti __cpp_rtti #elif defined(__GXX_RTTI) || defined (_CPPRTTI) -# define lest__cpp_rtti 1 +# define lest__cpp_rtti 1 #else -# define lest__cpp_rtti 0 +# define lest__cpp_rtti 0 #endif #if lest_FEATURE_REGEX_SEARCH # include #endif +// Stringify: + +#define lest_STRINGIFY( x ) lest_STRINGIFY_( x ) +#define lest_STRINGIFY_( x ) #x + // Compiler warning suppression: -#ifdef __clang__ +#if defined (__clang__) # pragma clang diagnostic ignored "-Waggregate-return" # pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-comparison" -#elif defined __GNUC__ +#elif defined (__GNUC__) # pragma GCC diagnostic ignored "-Waggregate-return" # pragma GCC diagnostic push #endif // Suppress shadow and unused-value warning for sections: -#if defined __clang__ +#if defined (__clang__) # define lest_SUPPRESS_WSHADOW _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) # define lest_SUPPRESS_WUNUSED _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wunused-value\"" ) # define lest_RESTORE_WARNINGS _Pragma( "clang diagnostic pop" ) -#elif defined __GNUC__ +#elif defined (__GNUC__) # define lest_SUPPRESS_WSHADOW _Pragma( "GCC diagnostic push" ) \ _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) # define lest_SUPPRESS_WUNUSED _Pragma( "GCC diagnostic push" ) \ @@ -104,12 +113,22 @@ # define lest_RESTORE_WARNINGS /*empty*/ #endif -#ifdef _MSVC_LANG -# define lest_CPP17_OR_GREATER_MS ( _MSVC_LANG >= 201703L ) -#else -# define lest_CPP17_OR_GREATER_MS 0 +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef lest_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define lest_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define lest_CPLUSPLUS __cplusplus +# endif #endif -# define lest_CPP17_OR_GREATER ( __cplusplus >= 201703L || lest_CPP17_OR_GREATER_MS ) + +#define lest_CPP98_OR_GREATER ( lest_CPLUSPLUS >= 199711L ) +#define lest_CPP11_OR_GREATER ( lest_CPLUSPLUS >= 201103L ) +#define lest_CPP14_OR_GREATER ( lest_CPLUSPLUS >= 201402L ) +#define lest_CPP17_OR_GREATER ( lest_CPLUSPLUS >= 201703L ) +#define lest_CPP20_OR_GREATER ( lest_CPLUSPLUS >= 202000L ) #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES ) # define MODULE lest_MODULE @@ -186,7 +205,7 @@ if ( lest::result score = lest_DECOMPOSE( expr ) ) \ throw lest::failure{ lest_LOCATION, #expr, score.decomposition }; \ else if ( lest_env.pass() ) \ - lest::report( lest_env.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, lest_env.context() ); \ + lest::report( lest_env.os, lest::passing{ lest_LOCATION, #expr, score.decomposition, lest_env.zen() }, lest_env.context() ); \ } \ catch(...) \ { \ @@ -201,7 +220,7 @@ if ( lest::result score = lest_DECOMPOSE( expr ) ) \ { \ if ( lest_env.pass() ) \ - lest::report( lest_env.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, lest_env.context() ); \ + lest::report( lest_env.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ), lest_env.zen() }, lest_env.context() ); \ } \ else \ throw lest::failure{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }; \ @@ -381,8 +400,8 @@ struct success : message struct passing : success { - passing( location where_, text expr_, text decomposition_ ) - : success( "passed", where_, expr_ + " for " + decomposition_) {} + passing( location where_, text expr_, text decomposition_, bool zen ) + : success( "passed", where_, expr_ + (zen ? "":" for " + decomposition_) ) {} }; struct got_none : success @@ -524,14 +543,45 @@ inline char const * sfx( char const * txt ) { return txt; } inline char const * sfx( char const * ) { return ""; } #endif -inline std::string to_string( std::nullptr_t ) { return "nullptr"; } -inline std::string to_string( std::string const & txt ) { return "\"" + txt + "\"" ; } +inline std::string transformed( char chr ) +{ + struct Tr { char chr; char const * str; } table[] = + { + {'\\', "\\\\" }, + {'\r', "\\r" }, {'\f', "\\f" }, + {'\n', "\\n" }, {'\t', "\\t" }, + }; + + for ( auto tr : table ) + { + if ( chr == tr.chr ) + return tr.str; + } + + auto unprintable = [](char c){ return 0 <= c && c < ' '; }; + + auto to_hex_string = [](char c) + { + std::ostringstream os; + os << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast( static_cast(c) ); + return os.str(); + }; + + return unprintable( chr ) ? to_hex_string( chr ) : std::string( 1, chr ); +} + +inline std::string make_tran_string( std::string const & txt ) { std::ostringstream os; for(auto c:txt) os << transformed(c); return os.str(); } +inline std::string make_strg_string( std::string const & txt ) { return "\"" + make_tran_string( txt ) + "\"" ; } +inline std::string make_char_string( char chr ) { return "\'" + make_tran_string( std::string( 1, chr ) ) + "\'" ; } + +inline std::string to_string( std::nullptr_t ) { return "nullptr"; } +inline std::string to_string( std::string const & txt ) { return make_strg_string( txt ); } #if lest_FEATURE_WSTRING inline std::string to_string( std::wstring const & txt ) ; #endif -inline std::string to_string( char const * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; } -inline std::string to_string( char * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; } +inline std::string to_string( char const * const txt ) { return txt ? make_strg_string( txt ) : "{null string}"; } +inline std::string to_string( char * const txt ) { return txt ? make_strg_string( txt ) : "{null string}"; } #if lest_FEATURE_WSTRING inline std::string to_string( wchar_t const * const txt ) { return txt ? to_string( std::wstring( txt ) ) : "{null string}"; } inline std::string to_string( wchar_t * const txt ) { return txt ? to_string( std::wstring( txt ) ) : "{null string}"; } @@ -550,29 +600,9 @@ inline std::string to_string( unsigned long long value ) { return make_value_ inline std::string to_string( double value ) { return make_value_string( value ) ; } inline std::string to_string( float value ) { return make_value_string( value ) + sfx("f" ); } -inline std::string to_string( signed char chr ) { return to_string( static_cast( chr ) ); } -inline std::string to_string( unsigned char chr ) { return to_string( static_cast( chr ) ); } - -inline std::string to_string( char chr ) -{ - struct Tr { char chr; char const * str; } table[] = - { - {'\r', "'\\r'" }, {'\f', "'\\f'" }, - {'\n', "'\\n'" }, {'\t', "'\\t'" }, - }; - - for ( auto tr : table ) - { - if ( chr == tr.chr ) - return tr.str; - } - - auto unprintable = [](char c){ return 0 <= c && c < ' '; }; - - return unprintable( chr ) - ? to_string( static_cast( chr ) ) - : "\'" + std::string( 1, chr ) + "\'" ; -} +inline std::string to_string( signed char chr ) { return make_char_string( static_cast( chr ) ); } +inline std::string to_string( unsigned char chr ) { return make_char_string( static_cast( chr ) ); } +inline std::string to_string( char chr ) { return make_char_string( chr ); } template< typename T > struct is_streamable @@ -968,7 +998,7 @@ inline bool select( text name, texts include ) inline int indefinite( int repeat ) { return repeat == -1; } -using seed_t = unsigned long; +using seed_t = std::mt19937::result_type; struct options { @@ -979,6 +1009,7 @@ struct options bool tags = false; bool time = false; bool pass = false; + bool zen = false; bool lexical = false; bool random = false; bool verbose = false; @@ -999,12 +1030,14 @@ struct env env & operator()( text test ) { - testing = test; return *this; + clear(); testing = test; return *this; } bool abort() { return opt.abort; } bool pass() { return opt.pass; } + bool zen() { return opt.zen; } + void clear() { ctx.clear(); } void pop() { ctx.pop_back(); } void push( text proposition ) { ctx.emplace_back( proposition ); } @@ -1311,6 +1344,7 @@ inline auto split_arguments( texts args ) -> std::tuple else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } else if ( opt == "-t" || "--time" == opt ) { option.time = true; continue; } else if ( opt == "-p" || "--pass" == opt ) { option.pass = true; continue; } + else if ( opt == "-z" || "--pass-zen" == opt ) { option.zen = true; continue; } else if ( opt == "-v" || "--verbose" == opt ) { option.verbose = true; continue; } else if ( "--version" == opt ) { option.version = true; continue; } else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } @@ -1322,6 +1356,8 @@ inline auto split_arguments( texts args ) -> std::tuple } in.push_back( arg ); } + option.pass = option.pass || option.zen; + return std::make_tuple( option, in ); } @@ -1337,6 +1373,7 @@ inline int usage( std::ostream & os ) " -g, --list-tags list tags of selected tests\n" " -l, --list-tests list selected tests\n" " -p, --pass also report passing tests\n" + " -z, --pass-zen ... without expansion\n" " -t, --time list duration of selected tests\n" " -v, --verbose also report passing or failing sections\n" " --order=declared use source code test order (default)\n" @@ -1437,9 +1474,9 @@ int run( test const (&specification)[N], int argc, char * argv[], std::ostream & } // namespace lest -#ifdef __clang__ +#if defined (__clang__) # pragma clang diagnostic pop -#elif defined __GNUC__ +#elif defined (__GNUC__) # pragma GCC diagnostic pop #endif From bfca604ab56272348ea7fdb3bddb6c03e9f089e6 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 28 Jun 2019 15:10:31 +0200 Subject: [PATCH 025/182] explicit permissions for install targets --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5016463..fbaf775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,10 +84,10 @@ add_custom_target( # handles install target install( - FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS WORLD_READ + FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS OWNER_READ GROUP_READ WORLD_READ ) install( FILES build/pfaedle DESTINATION bin - PERMISSIONS WORLD_EXECUTE COMPONENT binaries + PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries ) From 27da2a9c9ef24228d669f018825918ee35841180 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 23 Jul 2019 21:32:07 +0200 Subject: [PATCH 026/182] output -T geojson as WGS84 coordinates --- src/pfaedle/PfaedleMain.cpp | 6 +++++- src/pfaedle/eval/Collector.cpp | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 4c6e309..974cead 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -269,7 +269,11 @@ int main(int argc, char** argv) { auto l = shapeBuilder.shapeL(singleTrip); - o.print(l, util::json::Dict{{"ver", "new"}}); + // reproject to WGS84 to match RFC 7946 + for (auto& p : l) { + p = util::geo::webMercToLatLng(p.getX(), p.getY()); + } + o.flush(); pstr.close(); diff --git a/src/pfaedle/eval/Collector.cpp b/src/pfaedle/eval/Collector.cpp index 07c1ae8..eba44c5 100644 --- a/src/pfaedle/eval/Collector.cpp +++ b/src/pfaedle/eval/Collector.cpp @@ -110,6 +110,7 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS, gjout.print(oldL, util::json::Dict{{"ver", "old"}}); oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); } + for (auto newL : newSegs) { gjout.print(newL, util::json::Dict{{"ver", "new"}}); newLCut.insert(newLCut.end(), newL.begin(), newL.end()); From feacae92ef3b07b29753176170c86cc4678f529c Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 23 Jul 2019 22:47:35 +0200 Subject: [PATCH 027/182] output all GeoJSON as WGS84 to be RFC 7946 conform --- geo/pfaedle.qgs | 9558 +++++++++++--------- src/pfaedle/PfaedleMain.cpp | 8 +- src/pfaedle/eval/Collector.cpp | 4 +- src/pfaedle/router/Misc.h | 9 + src/pfaedle/router/ShapeBuilder.cpp | 2 +- src/util/geo/output/GeoGraphJsonOutput.h | 10 + src/util/geo/output/GeoGraphJsonOutput.tpp | 32 +- src/util/geo/output/GeoJsonOutput.h | 12 +- src/util/geo/output/GeoJsonOutput.tpp | 16 + 9 files changed, 5369 insertions(+), 4282 deletions(-) diff --git a/geo/pfaedle.qgs b/geo/pfaedle.qgs index edfabfd..2ec03bf 100644 --- a/geo/pfaedle.qgs +++ b/geo/pfaedle.qgs @@ -1,91 +1,70 @@ - + + - + + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - - - - meters - - 995121.91129447647836059 - 6259104.80718581937253475 - 1006900.15504457184579223 - 6270619.10718505643308163 - - 0 - 1 - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc - WGS84 - false - - - 0 - - - - - - - - - - OGRGeoJSON_Point20180203134333739 OGRGeoJSON_LineString20180203134333975 @@ -97,69 +76,106 @@ OSM_Transportation20181215024818603 OpenStreetMap_de20181215024846026 - + + + + + + + + + + + + + + + meters + + -374853.74009754881262779 + 4605645.85081499628722668 + 2640376.65992686524987221 + 7553306.65061968378722668 + + 0 + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo-Mercator + merc + WGS84 + false + + + 0 + + - - + + - + - + - + - + + - + - 875390.4375 - 6113024.5 - 875455.125 - 6113045.5 + 6.70734330570000026 + 47.04982400000000098 + 6.77521899999999988 + 47.07313500000000062 OGRGeoJSON_LineString20180203134333975 ./graph.json @@ -169,1294 +185,1740 @@ OGRGeoJSON LineString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - station_name - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + . 0 . @@ -1479,35 +1941,61 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + station_name + - + + + 6.70734330570000026 + 47.05522500000000008 + 6.74796900000000033 + 47.06722252349999991 + OGRGeoJSON_LineString20180206114956229 ./combgraph.json|layerid=0|subset="dummy" = 'no' @@ -1516,413 +2004,490 @@ def my_form_open(dialog, layer, feature): OGRGeoJSON LineString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - station_name - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + . 0 . @@ -1945,40 +2510,58 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + station_name + - + - 842634.8125 - 6090818 - 884707.25 - 6121615.5 + 6.70734330570000026 + 47.04982400000000098 + 6.77521899999999988 + 47.07313500000000062 OGRGeoJSON_Point20180203134333739 ./graph.json @@ -1988,494 +2571,625 @@ def my_form_open(dialog, layer, feature): OGRGeoJSON Point - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - station_name - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + . 0 . @@ -2498,42 +3212,65 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - + station_name + - + + + 6.70734330570000026 + 47.05522500000000008 + 6.74796900000000033 + 47.06722252349999991 + OGRGeoJSON_Point20180206114956218 ./combgraph.json @@ -2542,352 +3279,442 @@ def my_form_open(dialog, layer, feature): OGRGeoJSON Point - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - station_name - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + . 0 . @@ -2910,34 +3737,53 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + station_name + - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -2945,7 +3791,7 @@ def my_form_open(dialog, layer, feature): 20037508.34278924390673637 OSM_Transportation20181215024818603 - type=xyz&zmin=0&zmax=19&url=http://tile.thunderforest.com/transport/{z}/{x}/{y}.png + crs=EPSG:3857&format&type=xyz&url=http://tile.thunderforest.com/transport/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0 @@ -2957,33 +3803,70 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo Mercator + WGS 84 / Pseudo-Mercator merc WGS84 false - - - + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + wms - + - - + + + + 1 + 1 + 1 + + + + - + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + - + 0 - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -2991,7 +3874,7 @@ def my_form_open(dialog, layer, feature): 20037508.34278924390673637 OpenStreetMap_de20181215024846026 - type=xyz&zmin=0&zmax=18&url=http://a.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png + crs=EPSG:3857&format&type=xyz&url=http://a.tile.openstreetmap.de/tiles/osmde/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=18&zmin=0 @@ -3003,38 +3886,75 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo Mercator + WGS 84 / Pseudo-Mercator merc WGS84 false - - - + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + wms - + - - + + + + 1 + 1 + 1 + + + + - + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + - + 0 - + - 735429.75 - 5862813.5 - 738946.375 - 5867690 + 6.70738836720000009 + 47.05522500000000008 + 6.74579299999999993 + 47.06721104129999844 path20180217155708341 ./path.json @@ -3044,322 +3964,203 @@ def my_form_open(dialog, layer, feature): path - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 30 - ver - - - - - - + 0.7 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . - - - + + + + + + + + + + + - - + + + + + + + + - - . + + + + + . 0 . @@ -3382,17 +4183,13 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + - - - - - - - + ver - + trgraph_trgraph_LineString20180508200527144 ./trgraph.json|layerid=0|geometrytype=LineString @@ -3401,1270 +4198,1566 @@ def my_form_open(dialog, layer, feature): trgraph trgraph LineString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + - - + + + + + + + + - - . + + + + + . 0 . @@ -4687,15 +5780,13 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + - - - - - + id - + trgraph_trgraph_Point20180508200527256 ./trgraph.json|layerid=0|geometrytype=Point @@ -4704,260 +5795,155 @@ def my_form_open(dialog, layer, feature): trgraph trgraph Point - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat WGS84 - false + true + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + ogr + + - - + + - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + - - + + + + + + + + - - . + + + + + . 0 . @@ -4980,66 +5966,114 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout + + - - - - - + id + + + + + + + + + + + + + 255 + + + + + true + + + + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + EPSG:3857 + 3857 + 1 + + + meters + m2 + + + + + + false + + + + + + + + + + 30 + false + true + 0 + false + 0 + 16 + 50 + false + + false + + + 2 + D + true + + + + + + conditions unknown + + false - - - false - - - - - - - false - - - - - - 2 - true - D - - - false - - - false - - WGS84 - - 8 - false - - - - - - 0 - 240 - 240 - 255 - 255 255 + 255 240 + 0 + 240 + 255 + 240 + 90 + + + + - 2 + + 2 + 2 + 2 + 2 + 2 + 2 + 2 + + off OGRGeoJSON_LineString20180203134333975 OGRGeoJSON_LineString20180206114956229 @@ -5049,6 +6083,27 @@ def my_form_open(dialog, layer, feature): trgraph_trgraph_LineString20180508200527144 trgraph_trgraph_Point20180508200527256 + + + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + 0 + 2 + + to_vertex + to_vertex + to_vertex + to_vertex + to_vertex + to_vertex_and_segment + to_vertex_and_segment + enabled enabled @@ -5059,69 +6114,34 @@ def my_form_open(dialog, layer, feature): disabled current_layer - - - 2 - 2 - 2 - 2 - 2 - 2 - 2 - - - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex_and_segment - to_vertex_and_segment - - off - 0 - - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - - - + + WGS84 + + None - None - + false + + 8 false - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - EPSG:3857 - 3857 - 1 - - - - - - true - 255 - - - conditions unknown - 90 - - meters - m2 - - + + + + + + + + + + + + + + diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 974cead..e95bdd7 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -257,7 +257,7 @@ int main(int argc, char** argv) { 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); + out.printLatLng(*shapeBuilder.getGraph(), fstr); fstr.close(); } @@ -270,9 +270,7 @@ int main(int argc, char** argv) { auto l = shapeBuilder.shapeL(singleTrip); // reproject to WGS84 to match RFC 7946 - for (auto& p : l) { - p = util::geo::webMercToLatLng(p.getX(), p.getY()); - } + o.printLatLng(l, {}); o.flush(); pstr.close(); @@ -288,7 +286,7 @@ int main(int argc, char** argv) { 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); + out.printLatLng(ng, fstr); fstr.close(); } } catch (const xml::XmlFileException& ex) { diff --git a/src/pfaedle/eval/Collector.cpp b/src/pfaedle/eval/Collector.cpp index eba44c5..709830b 100644 --- a/src/pfaedle/eval/Collector.cpp +++ b/src/pfaedle/eval/Collector.cpp @@ -107,12 +107,12 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS, LINE newLCut; for (auto oldL : oldSegs) { - gjout.print(oldL, util::json::Dict{{"ver", "old"}}); + gjout.printLatLng(oldL, util::json::Dict{{"ver", "old"}}); oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); } for (auto newL : newSegs) { - gjout.print(newL, util::json::Dict{{"ver", "new"}}); + gjout.printLatLng(newL, util::json::Dict{{"ver", "new"}}); newLCut.insert(newLCut.end(), newL.begin(), newL.end()); } diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index c477b09..5cb1862 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -57,6 +57,7 @@ struct RoutingOpts { bool noSelfHops; }; +// _____________________________________________________________________________ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 && fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.01 && @@ -106,22 +107,27 @@ struct EdgeCost { double getValue() const { return _cost; } }; +// _____________________________________________________________________________ inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) { return EdgeCost(a.getValue() + b.getValue()); } +// _____________________________________________________________________________ inline bool operator<=(const EdgeCost& a, const EdgeCost& b) { return a.getValue() <= b.getValue(); } +// _____________________________________________________________________________ inline bool operator==(const EdgeCost& a, const EdgeCost& b) { return a.getValue() == b.getValue(); } +// _____________________________________________________________________________ inline bool operator>(const EdgeCost& a, const EdgeCost& b) { return a.getValue() > b.getValue(); } +// _____________________________________________________________________________ template inline bool angSmaller(const Point& f, const Point& m, const Point& t, double ang) { @@ -152,6 +158,7 @@ typedef std::vector EdgeListHops; typedef std::set MOTs; +// _____________________________________________________________________________ inline MOTs motISect(const MOTs& a, const MOTs& b) { MOTs ret; for (auto mot : a) @@ -159,6 +166,7 @@ inline MOTs motISect(const MOTs& a, const MOTs& b) { return ret; } +// _____________________________________________________________________________ inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed, const MOTs mots, const std::string& tid) { @@ -183,6 +191,7 @@ inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed, return ret; } +// _____________________________________________________________________________ inline std::string getMotStr(const MOTs& mots) { bool first = false; std::string motStr; diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index b96f8a6..898b5c4 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -130,7 +130,7 @@ EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr, LOG(INFO) << "Outputting combgraph.json..."; std::ofstream pstr(_cfg.dbgOutputPath + "/combgraph.json"); GeoGraphJsonOutput o; - o.print(g, pstr); + o.printLatLng(g, pstr); } return ret; diff --git a/src/util/geo/output/GeoGraphJsonOutput.h b/src/util/geo/output/GeoGraphJsonOutput.h index dcfb98b..83976ce 100644 --- a/src/util/geo/output/GeoGraphJsonOutput.h +++ b/src/util/geo/output/GeoGraphJsonOutput.h @@ -18,13 +18,23 @@ namespace output { class GeoGraphJsonOutput { public: inline GeoGraphJsonOutput(){}; + + // print a graph to the provided path template void print(const util::graph::Graph& outG, std::ostream& str); + // print a graph to the provided path, but treat coordinates as Web Mercator coordinates and reproject to WGS84 + template + void printLatLng(const util::graph::Graph& outG, std::ostream& str); + private: template Line createLine(const util::geo::Point& a, const util::geo::Point& b); + + // print a graph to the provided path + template + void printImpl(const util::graph::Graph& outG, std::ostream& str, bool proj); }; #include "util/geo/output/GeoGraphJsonOutput.tpp" diff --git a/src/util/geo/output/GeoGraphJsonOutput.tpp b/src/util/geo/output/GeoGraphJsonOutput.tpp index c6341cc..803a983 100644 --- a/src/util/geo/output/GeoGraphJsonOutput.tpp +++ b/src/util/geo/output/GeoGraphJsonOutput.tpp @@ -16,6 +16,20 @@ Line GeoGraphJsonOutput::createLine(const util::geo::Point& a, template void GeoGraphJsonOutput::print(const util::graph::Graph& outG, std::ostream& str) { + printImpl(outG, str, false); +} + +// _____________________________________________________________________________ +template +void GeoGraphJsonOutput::printLatLng(const util::graph::Graph& outG, + std::ostream& str) { + printImpl(outG, str, true); +} + +// _____________________________________________________________________________ +template +void GeoGraphJsonOutput::printImpl(const util::graph::Graph& outG, + std::ostream& str, bool proj) { GeoJsonOutput _out(str); // first pass, nodes @@ -30,7 +44,11 @@ void GeoGraphJsonOutput::print(const util::graph::Graph& outG, auto addProps = n->pl().getAttrs(); props.insert(addProps.begin(), addProps.end()); - _out.print(*n->pl().getGeom(), props); + if (proj) { + _out.printLatLng(*n->pl().getGeom(), props); + } else { + _out.print(*n->pl().getGeom(), props); + } } // second pass, edges @@ -50,11 +68,19 @@ void GeoGraphJsonOutput::print(const util::graph::Graph& outG, auto a = *e->getFrom()->pl().getGeom(); if (e->getTo()->pl().getGeom()) { auto b = *e->getTo()->pl().getGeom(); - _out.print(createLine(a, b), props); + if (proj) { + _out.printLatLng(createLine(a, b), props); + } else { + _out.print(createLine(a, b), props); + } } } } else { - _out.print(*e->pl().getGeom(), props); + if (proj) { + _out.printLatLng(*e->pl().getGeom(), props); + } else { + _out.print(*e->pl().getGeom(), props); + } } } } diff --git a/src/util/geo/output/GeoJsonOutput.h b/src/util/geo/output/GeoJsonOutput.h index f4a8492..701521b 100644 --- a/src/util/geo/output/GeoJsonOutput.h +++ b/src/util/geo/output/GeoJsonOutput.h @@ -5,9 +5,9 @@ #ifndef UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_ #define UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_ +#include #include #include -#include #include "util/String.h" #include "util/geo/Geo.h" #include "util/json/Writer.h" @@ -21,10 +21,19 @@ class GeoJsonOutput { GeoJsonOutput(std::ostream& str); GeoJsonOutput(std::ostream& str, json::Val attrs); ~GeoJsonOutput(); + template void print(const Point& p, json::Val attrs); + template void print(const Line& l, json::Val attrs); + + template + void printLatLng(const Point& p, json::Val attrs); + + template + void printLatLng(const Line& l, json::Val attrs); + void flush(); private: @@ -32,7 +41,6 @@ class GeoJsonOutput { }; #include "util/geo/output/GeoJsonOutput.tpp" - } } } diff --git a/src/util/geo/output/GeoJsonOutput.tpp b/src/util/geo/output/GeoJsonOutput.tpp index 1f599cf..09f5c2a 100644 --- a/src/util/geo/output/GeoJsonOutput.tpp +++ b/src/util/geo/output/GeoJsonOutput.tpp @@ -46,3 +46,19 @@ void GeoJsonOutput::print(const Line& line, json::Val attrs) { _wr.val(attrs); _wr.close(); } + +// _____________________________________________________________________________ +template +void GeoJsonOutput::printLatLng(const Point& p, json::Val attrs) { + auto projP = util::geo::webMercToLatLng(p.getX(), p.getY()); + print(projP, attrs); +} + +// _____________________________________________________________________________ +template +void GeoJsonOutput::printLatLng(const Line& line, json::Val attrs) { + Line projL; + for (auto p : line) projL.push_back(util::geo::webMercToLatLng(p.getX(), p.getY())); + + print(projL, attrs); +} From 06aefa538ab2610842ba5eb575fa90b94e0870b2 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 6 Sep 2019 01:24:24 +0200 Subject: [PATCH 028/182] upate util --- src/util/String.h | 61 +++++++++++++++++++++++++++++----------- src/util/http/Server.cpp | 8 +++++- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/util/String.h b/src/util/String.h index b2dad5e..536b713 100644 --- a/src/util/String.h +++ b/src/util/String.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -36,18 +37,43 @@ inline std::string urlDecode(const std::string& encoded) { } // _____________________________________________________________________________ -inline std::string jsonStringEscape(const std::string& unescaped) { - std::string escaped; - for (size_t i = 0; i < unescaped.size(); ++i) { - if (unescaped[i] == '"' || unescaped[i] == '\\') { - escaped += "\\"; +inline std::string jsonStringEscape(const std::string& unesc) { + // modified code from + // http://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c + std::ostringstream o; + for (auto c = unesc.cbegin(); c != unesc.cend(); c++) { + switch (*c) { + case '"': + o << "\\\""; + break; + case '\\': + o << "\\\\"; + break; + case '\b': + o << "\\b"; + break; + case '\f': + o << "\\f"; + break; + case '\n': + o << "\\n"; + break; + case '\r': + o << "\\r"; + break; + case '\t': + o << "\\t"; + break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + o << "\\u" << std::hex << std::setw(4) << std::setfill('0') + << static_cast(*c); + } else { + o << *c; + } } - if (iscntrl(unescaped[i])) { - escaped += " "; - } - escaped += unescaped[i]; } - return escaped; + return o.str(); } // _____________________________________________________________________________ @@ -152,19 +178,22 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s, d[0] = 0; for (size_t i = 1; i <= len1; ++i) d[i * (len2 + 1)] = i; - for (size_t i = 1; i <= len2; ++i) d[ i] = i; + for (size_t i = 1; i <= len2; ++i) d[i] = i; for (size_t i = 1; i <= len1; i++) { for (size_t j = 1; j <= len2; j++) { - d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1, d[i * (len2 + 1) + j - 1] + 1), - d[(i - 1) * (len2 + 1) + j - 1] + (prefix[i - 1] == s[j - 1] ? 0 : 1)); + d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1, + d[i * (len2 + 1) + j - 1] + 1), + d[(i - 1) * (len2 + 1) + j - 1] + + (prefix[i - 1] == s[j - 1] ? 0 : 1)); } } // take min of last row size_t deltaMin = std::max(std::max(deltaMax + 1, prefix.size()), s.size()); for (size_t i = 0; i <= len2; i++) { - if (d[len1 * (len2 + 1) + i] < deltaMin) deltaMin = d[len1 * (len2 + 1) + i]; + if (d[len1 * (len2 + 1) + i] < deltaMin) + deltaMin = d[len1 * (len2 + 1) + i]; } return deltaMin; @@ -177,13 +206,13 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s) { // _____________________________________________________________________________ inline std::string toUpper(std::string str) { - std::transform(str.begin(), str.end(),str.begin(), toupper); + std::transform(str.begin(), str.end(), str.begin(), toupper); return str; } // _____________________________________________________________________________ inline std::string toLower(std::string str) { - std::transform(str.begin(), str.end(),str.begin(), tolower); + std::transform(str.begin(), str.end(), str.begin(), tolower); return str; } diff --git a/src/util/http/Server.cpp b/src/util/http/Server.cpp index fa20a97..d35166c 100644 --- a/src/util/http/Server.cpp +++ b/src/util/http/Server.cpp @@ -22,6 +22,7 @@ #include #include "Server.h" #include "util/String.h" +#include "util/log/Log.h" using util::http::Socket; using util::http::Queue; @@ -112,7 +113,12 @@ void HttpServer::handle() { answ = Answer("500 Internal Server Error", "500 Internal Server Error"); } - send(connection, &answ); + try { + send(connection, &answ); + } catch (const std::runtime_error& err) { + LOG(WARN) << err.what(); + } + close(connection); } } From a5931eb9eca9bf347c05a93654c0b82bdf622c7a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 6 Sep 2019 15:19:11 +0200 Subject: [PATCH 029/182] update util --- src/util/geo/Geo.h | 2 +- src/util/geo/PolyLine.tpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index 1cb696e..ffbfada 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -1277,7 +1277,7 @@ inline size_t convexHullImpl(const MultiPoint& a, size_t p1, size_t p2, for (const auto& p : a) { double tmpDist = distToSegment((*h)[p1], (*h)[p2], p); double cp = crossProd(p, LineSegment((*h)[p1], (*h)[p2])); - if ((cp > 0) && tmpDist > maxDist) { + if ((cp > 0 + EPSILON) && tmpDist > maxDist) { pa = p; found = true; maxDist = tmpDist; diff --git a/src/util/geo/PolyLine.tpp b/src/util/geo/PolyLine.tpp index 8d34c14..1563874 100644 --- a/src/util/geo/PolyLine.tpp +++ b/src/util/geo/PolyLine.tpp @@ -499,17 +499,23 @@ SharedSegments PolyLine::getSharedSegments(const PolyLine& pl, double totalDist = dist(s, e); while (curSegDist <= totalDist) { - const Point& curPointer = interpolate(s, e, curSegDist); + const auto& curPointer = interpolate(s, e, curSegDist); if (pl.distTo(curPointer) <= dmax) { LinePoint curCmpPointer = pl.projectOn(curPointer); LinePoint curBackProjectedPointer = projectOn(curCmpPointer.p); + + skips = 0; if (in) { curEndCand = curBackProjectedPointer; curEndCandCmp = curCmpPointer; + if (curEndCand.totalPos < curStartCand.totalPos) { + curEndCand = curStartCand; + } + single = false; comp = fabs(curStartCand.totalPos * length - @@ -530,6 +536,7 @@ SharedSegments PolyLine::getSharedSegments(const PolyLine& pl, curEndCand.totalPos * length) > MIN_SEG_LENGTH && fabs(curStartCandCmp.totalPos * plLength - curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) { + assert(curStartCand.totalPos < curEndCand.totalPos); ret.segments.push_back( SharedSegment(std::pair, LinePoint>( curStartCand, curStartCandCmp), @@ -566,6 +573,7 @@ SharedSegments PolyLine::getSharedSegments(const PolyLine& pl, MIN_SEG_LENGTH && fabs(curStartCandCmp.totalPos * plLength - curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) { + assert(curStartCand.totalPos < curEndCand.totalPos); ret.segments.push_back(SharedSegment( std::pair, LinePoint>(curStartCand, curStartCandCmp), std::pair, LinePoint>(curEndCand, curEndCandCmp))); From 31f9cb72ff39487da88a446c7447b5b1d0e973eb Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 16 Sep 2019 00:39:17 +0200 Subject: [PATCH 030/182] refactoring --- src/CMakeLists.txt | 1 - src/cppgtfs | 2 +- src/pfaedle/CMakeLists.txt | 2 +- src/pfaedle/PfaedleMain.cpp | 4 +- src/pfaedle/osm/OsmBuilder.cpp | 74 +++++++++++++++++----------------- src/pfaedle/osm/OsmBuilder.h | 28 ++++++------- src/xml | 2 +- 7 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bb21a8..7d54350 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,5 +11,4 @@ include_directories( add_subdirectory(util) add_subdirectory(pfaedle) add_subdirectory(cppgtfs) -add_subdirectory(xml) add_subdirectory(configparser) diff --git a/src/cppgtfs b/src/cppgtfs index f5391e1..c704a61 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit f5391e1567f4f21379f7b1fb6d82fc904a61c434 +Subproject commit c704a610d91ec2be575c42a760cc6157c8fdf932 diff --git a/src/pfaedle/CMakeLists.txt b/src/pfaedle/CMakeLists.txt index 5963842..271f2c6 100644 --- a/src/pfaedle/CMakeLists.txt +++ b/src/pfaedle/CMakeLists.txt @@ -17,4 +17,4 @@ add_executable(pfaedle ${pfaedle_main}) add_library(pfaedle_dep ${pfaedle_SRC}) include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) -target_link_libraries(pfaedle pfaedle_dep util xml configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread) +target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread) diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index e95bdd7..55bea77 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -178,7 +178,7 @@ int main(int argc, char** argv) { } try { osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box); - } catch (const xml::XmlFileException& ex) { + } catch (const pfxml::parse_exc& ex) { LOG(ERROR) << "Could not parse OSM data, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::OSM_PARSE_ERR)); @@ -289,7 +289,7 @@ int main(int argc, char** argv) { out.printLatLng(ng, fstr); fstr.close(); } - } catch (const xml::XmlFileException& ex) { + } catch (const pfxml::parse_exc& ex) { LOG(ERROR) << "Could not parse OSM data, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::OSM_PARSE_ERR)); diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index 01df3d9..eaa4ab1 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -22,7 +22,7 @@ #include "util/Misc.h" #include "util/Nullable.h" #include "util/log/Log.h" -#include "xml/File.h" +#include "xml/pfxml.h" using util::geo::webMercMeterDist; using util::geo::Box; @@ -85,7 +85,7 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, OsmFilter filter(opts); - xml::File xml(path); + pfxml::file xml(path); // we do four passes of the file here to be as memory creedy as possible: // - the first pass collects all node IDs which are @@ -103,8 +103,8 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, LOG(VDEBUG) << "Reading bounding box nodes..."; skipUntil(&xml, "node"); - xml::ParserState nodeBeg = xml.state(); - xml::ParserState edgesBeg = + pfxml::parser_state nodeBeg = xml.state(); + pfxml::parser_state edgesBeg = readBBoxNds(&xml, &bboxNodes, &noHupNodes, filter, bbox); LOG(VDEBUG) << "Reading relations..."; @@ -113,13 +113,13 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, &rawRests); LOG(VDEBUG) << "Reading edges..."; - xml.setState(edgesBeg); + xml.set_state(edgesBeg); readEdges(&xml, g, intmRels, wayRels, filter, bboxNodes, &nodes, &multNodes, noHupNodes, attrKeys[1], rawRests, res, intmRels.flat, &eTracks, opts); LOG(VDEBUG) << "Reading kept nodes..."; - xml.setState(nodeBeg); + xml.set_state(nodeBeg); readNodes(&xml, g, intmRels, nodeRels, filter, bboxNodes, &nodes, &multNodes, &orphanStations, attrKeys[0], intmRels.flat, opts); } @@ -283,7 +283,7 @@ void OsmBuilder::filterWrite(const std::string& in, const std::string& out, // always empty NIdMultMap multNodes; - xml::File xml(in); + pfxml::file xml(in); std::ofstream outstr; outstr.open(out); @@ -310,18 +310,18 @@ void OsmBuilder::filterWrite(const std::string& in, const std::string& out, } skipUntil(&xml, "node"); - xml::ParserState nodeBeg = xml.state(); - xml::ParserState edgesBeg = + pfxml::parser_state nodeBeg = xml.state(); + pfxml::parser_state edgesBeg = readBBoxNds(&xml, &bboxNodes, &noHupNodes, filter, latLngBox); skipUntil(&xml, "relation"); readRels(&xml, &rels, &nodeRels, &wayRels, filter, attrKeys[2], &rests); - xml.setState(edgesBeg); + xml.set_state(edgesBeg); readEdges(&xml, wayRels, filter, bboxNodes, attrKeys[1], &ways, &nodes, rels.flat); - xml.setState(nodeBeg); + xml.set_state(nodeBeg); readWriteNds(&xml, &wr, nodeRels, filter, bboxNodes, &nodes, attrKeys[0], rels.flat); @@ -335,7 +335,7 @@ void OsmBuilder::filterWrite(const std::string& in, const std::string& out, } // _____________________________________________________________________________ -void OsmBuilder::readWriteRels(xml::File* i, util::xml::XmlWriter* o, +void OsmBuilder::readWriteRels(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways, NIdMap* nodes, const OsmFilter& filter, const AttrKeySet& keepAttrs) { @@ -386,7 +386,7 @@ void OsmBuilder::readWriteRels(xml::File* i, util::xml::XmlWriter* o, for (const auto& kv : rel.attrs) { std::map attrs = { - {"k", kv.first}, {"v", xml::File::decode(kv.second)}}; + {"k", kv.first}, {"v", pfxml::file::decode(kv.second)}}; o->openTag("tag", attrs); o->closeTag(); } @@ -397,7 +397,7 @@ void OsmBuilder::readWriteRels(xml::File* i, util::xml::XmlWriter* o, } // _____________________________________________________________________________ -void OsmBuilder::readWriteWays(xml::File* i, util::xml::XmlWriter* o, +void OsmBuilder::readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways, const AttrKeySet& keepAttrs) const { OsmWay w; @@ -413,7 +413,7 @@ void OsmBuilder::readWriteWays(xml::File* i, util::xml::XmlWriter* o, for (const auto& kv : w.attrs) { std::map attrs; attrs["k"] = kv.first; - attrs["v"] = xml::File::decode(kv.second); + attrs["v"] = pfxml::file::decode(kv.second); o->openTag("tag", attrs); o->closeTag(); } @@ -441,7 +441,7 @@ NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { } // _____________________________________________________________________________ -xml::ParserState OsmBuilder::readBBoxNds(xml::File* xml, OsmIdSet* nodes, +pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, OsmIdSet* nohupNodes, const OsmFilter& filter, const BBoxIdx& bbox) const { @@ -449,7 +449,7 @@ xml::ParserState OsmBuilder::readBBoxNds(xml::File* xml, OsmIdSet* nodes, uint64_t curId = 0; do { - const xml::Tag& cur = xml->get(); + const pfxml::tag& cur = xml->get(); if (inNodeBlock && xml->level() == 3 && curId && strcmp(cur.name, "tag") == 0) { @@ -479,12 +479,12 @@ xml::ParserState OsmBuilder::readBBoxNds(xml::File* xml, OsmIdSet* nodes, } // _____________________________________________________________________________ -OsmWay OsmBuilder::nextWayWithId(xml::File* xml, osmid wid, +OsmWay OsmBuilder::nextWayWithId(pfxml::file* xml, osmid wid, const AttrKeySet& keepAttrs) const { OsmWay w; do { - const xml::Tag& cur = xml->get(); + const pfxml::tag& cur = xml->get(); if (xml->level() == 2 || xml->level() == 0) { if (w.id || strcmp(cur.name, "way")) return w; @@ -508,7 +508,7 @@ OsmWay OsmBuilder::nextWayWithId(xml::File* xml, osmid wid, } // _____________________________________________________________________________ -void OsmBuilder::skipUntil(xml::File* xml, const std::string& s) const { +void OsmBuilder::skipUntil(pfxml::file* xml, const std::string& s) const { while (xml->next() && strcmp(xml->get().name, s.c_str())) { } } @@ -529,14 +529,14 @@ bool OsmBuilder::relKeep(osmid id, const RelMap& rels, } // _____________________________________________________________________________ -OsmWay OsmBuilder::nextWay(xml::File* xml, const RelMap& wayRels, +OsmWay OsmBuilder::nextWay(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, const FlatRels& fl) const { OsmWay w; do { - const xml::Tag& cur = xml->get(); + const pfxml::tag& cur = xml->get(); if (xml->level() == 2 || xml->level() == 0) { if (keepWay(w, wayRels, filter, bBoxNodes, fl)) return w; if (strcmp(cur.name, "way")) return OsmWay(); @@ -579,7 +579,7 @@ bool OsmBuilder::keepWay(const OsmWay& w, const RelMap& wayRels, } // _____________________________________________________________________________ -void OsmBuilder::readEdges(xml::File* xml, const RelMap& wayRels, +void OsmBuilder::readEdges(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, OsmIdList* ret, NIdMap* nodes, const FlatRels& flat) { @@ -593,7 +593,7 @@ void OsmBuilder::readEdges(xml::File* xml, const RelMap& wayRels, } // _____________________________________________________________________________ -void OsmBuilder::readEdges(xml::File* xml, Graph* g, const RelLst& rels, +void OsmBuilder::readEdges(pfxml::file* xml, Graph* g, const RelLst& rels, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, NIdMap* nodes, NIdMultMap* multiNodes, const OsmIdSet& noHupNodes, @@ -675,7 +675,7 @@ void OsmBuilder::processRestr(osmid nid, osmid wid, } // _____________________________________________________________________________ -OsmNode OsmBuilder::nextNode(xml::File* xml, NIdMap* nodes, +OsmNode OsmBuilder::nextNode(pfxml::file* xml, NIdMap* nodes, NIdMultMap* multNodes, const RelMap& nodeRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, @@ -683,7 +683,7 @@ OsmNode OsmBuilder::nextNode(xml::File* xml, NIdMap* nodes, OsmNode n; do { - const xml::Tag& cur = xml->get(); + const pfxml::tag& cur = xml->get(); if (xml->level() == 2 || xml->level() == 0) { if (keepNode(n, *nodes, *multNodes, nodeRels, bBoxNodes, filter, fl)) return n; @@ -725,7 +725,7 @@ bool OsmBuilder::keepNode(const OsmNode& n, const NIdMap& nodes, } // _____________________________________________________________________________ -void OsmBuilder::readWriteNds(xml::File* i, util::xml::XmlWriter* o, +void OsmBuilder::readWriteNds(pfxml::file* i, util::xml::XmlWriter* o, const RelMap& nRels, const OsmFilter& filter, const OsmIdSet& bBoxNds, NIdMap* nds, const AttrKeySet& keepAttrs, @@ -739,7 +739,7 @@ void OsmBuilder::readWriteNds(xml::File* i, util::xml::XmlWriter* o, {"lat", std::to_string(nd.lat)}, {"lon", std::to_string(nd.lng)}}); for (const auto& kv : nd.attrs) { - o->openTag("tag", {{"k", kv.first}, {"v", xml::File::decode(kv.second)}}); + o->openTag("tag", {{"k", kv.first}, {"v", pfxml::file::decode(kv.second)}}); o->closeTag(); } o->closeTag(); @@ -747,7 +747,7 @@ void OsmBuilder::readWriteNds(xml::File* i, util::xml::XmlWriter* o, } // _____________________________________________________________________________ -void OsmBuilder::readNodes(xml::File* xml, Graph* g, const RelLst& rels, +void OsmBuilder::readNodes(pfxml::file* xml, Graph* g, const RelLst& rels, const RelMap& nodeRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, NIdMap* nodes, NIdMultMap* multNodes, NodeSet* orphanStations, @@ -799,12 +799,12 @@ void OsmBuilder::readNodes(xml::File* xml, Graph* g, const RelLst& rels, } // _____________________________________________________________________________ -OsmRel OsmBuilder::nextRel(xml::File* xml, const OsmFilter& filter, +OsmRel OsmBuilder::nextRel(pfxml::file* xml, const OsmFilter& filter, const AttrKeySet& keepAttrs) const { OsmRel rel; do { - const xml::Tag& cur = xml->get(); + const pfxml::tag& cur = xml->get(); if (xml->level() == 2 || xml->level() == 0) { uint64_t keepFlags = 0; uint64_t dropFlags = 0; @@ -873,7 +873,7 @@ OsmRel OsmBuilder::nextRel(xml::File* xml, const OsmFilter& filter, } // _____________________________________________________________________________ -void OsmBuilder::readRels(xml::File* xml, RelLst* rels, RelMap* nodeRels, +void OsmBuilder::readRels(pfxml::file* xml, RelLst* rels, RelMap* nodeRels, RelMap* wayRels, const OsmFilter& filter, const AttrKeySet& keepAttrs, Restrictions* rests) const { @@ -940,7 +940,7 @@ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, const Normalizer& norm) const { std::string ret; for (const auto& s : rule) { - ret = norm(xml::File::decode(getAttr(s, id, attrs, entRels, rels))); + ret = norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); if (!ret.empty()) return ret; } @@ -954,7 +954,7 @@ std::vector OsmBuilder::getAttrMatchRanked( std::vector ret; for (const auto& s : rule) { std::string tmp = - norm(xml::File::decode(getAttr(s, id, attrs, entRels, rels))); + norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); if (!tmp.empty()) ret.push_back(tmp); } @@ -1425,7 +1425,7 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.sNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.shortName = ops.lineNormzer(xml::File::decode(relAttr.second)); + el.shortName = ops.lineNormzer(pfxml::file::decode(relAttr.second)); if (!el.shortName.empty()) found = true; } } @@ -1436,7 +1436,7 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.fromNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.fromStr = ops.statNormzer(xml::File::decode(relAttr.second)); + el.fromStr = ops.statNormzer(pfxml::file::decode(relAttr.second)); if (!el.fromStr.empty()) found = true; } } @@ -1447,7 +1447,7 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.toNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.toStr = ops.statNormzer(xml::File::decode(relAttr.second)); + el.toStr = ops.statNormzer(pfxml::file::decode(relAttr.second)); if (!el.toStr.empty()) found = true; } } diff --git a/src/pfaedle/osm/OsmBuilder.h b/src/pfaedle/osm/OsmBuilder.h index fdcb764..8b4c54f 100644 --- a/src/pfaedle/osm/OsmBuilder.h +++ b/src/pfaedle/osm/OsmBuilder.h @@ -25,7 +25,7 @@ #include "util/Nullable.h" #include "util/geo/Geo.h" #include "util/xml/XmlWriter.h" -#include "xml/File.h" +#include "xml/pfxml.h" namespace pfaedle { namespace osm { @@ -102,37 +102,37 @@ class OsmBuilder { const std::vector& opts, const BBoxIdx& box); private: - xml::ParserState readBBoxNds(xml::File* xml, OsmIdSet* nodes, + pfxml::parser_state readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, OsmIdSet* noHupNodes, const OsmFilter& filter, const BBoxIdx& bbox) const; - void readRels(xml::File* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels, + void readRels(pfxml::file* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels, const OsmFilter& filter, const AttrKeySet& keepAttrs, Restrictions* rests) const; void readRestr(const OsmRel& rel, Restrictions* rests, const OsmFilter& filter) const; - void readNodes(xml::File* f, Graph* g, const RelLst& rels, + void readNodes(pfxml::file* f, Graph* g, const RelLst& rels, const RelMap& nodeRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, NIdMap* nodes, NIdMultMap* multNodes, NodeSet* orphanStations, const AttrKeySet& keepAttrs, const FlatRels& flatRels, const OsmReadOpts& opts) const; - void readWriteNds(xml::File* i, util::xml::XmlWriter* o, + void readWriteNds(pfxml::file* i, util::xml::XmlWriter* o, const RelMap& nodeRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, NIdMap* nodes, const AttrKeySet& keepAttrs, const FlatRels& f) const; - void readWriteWays(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways, + void readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways, const AttrKeySet& keepAttrs) const; - void readWriteRels(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways, + void readWriteRels(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways, NIdMap* nodes, const OsmFilter& filter, const AttrKeySet& keepAttrs); - void readEdges(xml::File* xml, Graph* g, const RelLst& rels, + void readEdges(pfxml::file* xml, Graph* g, const RelLst& rels, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, NIdMap* nodes, NIdMultMap* multNodes, const OsmIdSet& noHupNodes, @@ -140,21 +140,21 @@ class OsmBuilder { Restrictor* restor, const FlatRels& flatRels, EdgTracks* etracks, const OsmReadOpts& opts); - void readEdges(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter, + void readEdges(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels); - OsmWay nextWay(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter, + OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, const FlatRels& flatRels) const; bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const FlatRels& fl) const; - OsmWay nextWayWithId(xml::File* xml, osmid wid, + OsmWay nextWayWithId(pfxml::file* xml, osmid wid, const AttrKeySet& keepAttrs) const; - OsmNode nextNode(xml::File* xml, NIdMap* nodes, NIdMultMap* multNodes, + OsmNode nextNode(pfxml::file* xml, NIdMap* nodes, NIdMultMap* multNodes, const RelMap& nodeRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, const FlatRels& flatRels) const; @@ -164,7 +164,7 @@ class OsmBuilder { const OsmIdSet& bBoxNodes, const OsmFilter& filter, const FlatRels& fl) const; - OsmRel nextRel(xml::File* xml, const OsmFilter& filter, + OsmRel nextRel(pfxml::file* xml, const OsmFilter& filter, const AttrKeySet& keepAttrs) const; protected: @@ -233,7 +233,7 @@ class OsmBuilder { void getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const; - void skipUntil(xml::File* xml, const std::string& s) const; + void skipUntil(pfxml::file* xml, const std::string& s) const; void processRestr(osmid nid, osmid wid, const Restrictions& rawRests, Edge* e, Node* n, Restrictor* restor) const; diff --git a/src/xml b/src/xml index 5081d32..23cc358 160000 --- a/src/xml +++ b/src/xml @@ -1 +1 @@ -Subproject commit 5081d32879c30456f6cb515342a3096c5a0d7de6 +Subproject commit 23cc358256df7f567cb249736e216c01b65aaf6f From 9262b3ad45f3c487252ceed7d78d5ceabfc4313d Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 16 Sep 2019 00:42:07 +0200 Subject: [PATCH 031/182] update xml submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 6bd5f05..826a1a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://ad-git.informatik.uni-freiburg.de/ad/cppgtfs.git [submodule "src/xml"] path = src/xml - url = https://git.patrickbrosi.de/patrick/xmlparser + url = https://github.com/patrickbr/pfxml.git [submodule "src/configparser"] path = src/configparser url = https://git.patrickbrosi.de/patrick/configparser From f856e9865c82135074efd26270861b40e3578e7f Mon Sep 17 00:00:00 2001 From: Benitoite Date: Sun, 9 Feb 2020 04:47:13 -0800 Subject: [PATCH 032/182] CMakeLists.txt: download submodules if needed Download git submodules if the CMakeLists.txt files for the configparser or cppgtfs directories are not present. --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbaf775..86c68e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,14 @@ else() set(VERSION_GIT_FULL "${VERSION_GIT}-${VERSION_GIT_IS_DIRTY}") endif() +# Download submodules if needed + +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/CMakeLists.txt) + execute_process( + COMMAND git submodule update --init --recursive + ) +endif() + add_subdirectory(src) # tests From 6d2671900b82b1ad2719d68c6d577a7a9f5afa23 Mon Sep 17 00:00:00 2001 From: Benitoite Date: Sun, 9 Feb 2020 04:52:18 -0800 Subject: [PATCH 033/182] also check for xml submodule --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86c68e7..c9ac6c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ endif() # Download submodules if needed -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/CMakeLists.txt) +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/xml/pfxml.h) execute_process( COMMAND git submodule update --init --recursive ) From 3eb5c881fe345e58f771bedfdf5c81c3c39505c3 Mon Sep 17 00:00:00 2001 From: Benitoite Date: Mon, 10 Feb 2020 07:10:46 -0800 Subject: [PATCH 034/182] Detect .git instead of CMakeLists.txt in submods --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9ac6c7..008bd55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,10 +70,10 @@ endif() # Download submodules if needed -if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/CMakeLists.txt OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/xml/pfxml.h) - execute_process( - COMMAND git submodule update --init --recursive - ) +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/.git OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/.git OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/xml/.git) + execute_process( + COMMAND git submodule update --init --recursive + ) endif() add_subdirectory(src) From 6c42f8244ebd8c1ee2f3e5ef27ffb96ff5869c73 Mon Sep 17 00:00:00 2001 From: Alexander Matheisen Date: Wed, 26 Feb 2020 11:36:34 +0100 Subject: [PATCH 035/182] Fix name of config param --- src/pfaedle/config/MotConfigReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pfaedle/config/MotConfigReader.cpp b/src/pfaedle/config/MotConfigReader.cpp index 4dad419..bffe3a7 100644 --- a/src/pfaedle/config/MotConfigReader.cpp +++ b/src/pfaedle/config/MotConfigReader.cpp @@ -408,7 +408,7 @@ void MotConfigReader::parse(const std::vector& paths) { p.getVal(secStr, "station_id_normalize_chain").pos, "", std::string("", - p.getVal(secStr, "station_normalize_chain").file); + p.getVal(secStr, "station_id_normalize_chain").file); } } From 69dc466605654c230da1a583b0e2f833666cf939 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 26 May 2020 10:23:24 +0200 Subject: [PATCH 036/182] remove empty variable from link --- src/pfaedle/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pfaedle/CMakeLists.txt b/src/pfaedle/CMakeLists.txt index 271f2c6..22b083a 100644 --- a/src/pfaedle/CMakeLists.txt +++ b/src/pfaedle/CMakeLists.txt @@ -17,4 +17,4 @@ add_executable(pfaedle ${pfaedle_main}) add_library(pfaedle_dep ${pfaedle_SRC}) include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) -target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread) +target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs -lpthread) From c023bb963d84573ab38d03f8236a1d1bf4acb031 Mon Sep 17 00:00:00 2001 From: Alexander Held | geOps Date: Wed, 30 Sep 2020 16:20:04 +0200 Subject: [PATCH 037/182] Fix edge splitting fragment being registered with wrong geometry in edge grid If I'm not mistaken, there is a typo in the OsmBuilder when an edge is split into two edges to insert a station: one of the new edges is registered with the wrong geometry in the edge grid. This should fix it. --- src/pfaedle/osm/OsmBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index eaa4ab1..aae4ee3 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -1357,7 +1357,7 @@ std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, ll.push_back(*n->pl().getGeom()); ll.push_back(*e->getTo()->pl().getGeom()); *nf->pl().getGeom() = ll; - eg->add(l, nf); + eg->add(ll, nf); // replace edge in restrictor restor->replaceEdge(e, ne, nf); From 08b0685ad1f1d87d3c43fcd16520af9a7d36c7f5 Mon Sep 17 00:00:00 2001 From: Alexander Held Date: Tue, 13 Oct 2020 20:27:20 +0200 Subject: [PATCH 038/182] Optional transit line penalties when no explicit line was given (#19) * Enable transit line penalties also when no explicit line was given Currently, if no transit line is given, all edges get penalty factor 0 regardless of whether there are transit lines following the edge or not. The change introduced in this commit will give penalty factor 0 if there is at least one transit line following the edge and penalty factor 1 if there are no tranist lines at all following the edge. Behavior in the case when a transit line is given is untouched. This should improve routing results by preferring edges with associated transit lines. This used to be the behaviour before some refactoring. Maybe it was broken unintentionally. --- pfaedle.cfg | 30 ++++++++++++++++++++++++++ src/pfaedle/config/MotConfigReader.cpp | 6 ++++++ src/pfaedle/router/Misc.h | 6 +++++- src/pfaedle/router/Router.cpp | 15 +++++++------ src/pfaedle/router/Router.h | 4 ++-- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/pfaedle.cfg b/pfaedle.cfg index 346f463..8840534 100644 --- a/pfaedle.cfg +++ b/pfaedle.cfg @@ -411,6 +411,11 @@ routing_one_way_meter_punish_fac: 1 # information routing_line_unmatched_punish_fac: 1 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + # special line normalization for trains line_normalize_chain: , -> ' '; @@ -793,6 +798,11 @@ routing_one_way_edge_punish: 5000 # information # routing_line_unmatched_punish_fac: 1.75 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + [coach] # OSM entities to keep on different levels, as k=v. Applies @@ -1011,6 +1021,11 @@ routing_one_way_meter_punish_fac: 1 # information routing_line_unmatched_punish_fac: 0.5 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + [gondola] # OSM entities to keep on different levels, as k=v. Applies @@ -1152,6 +1167,11 @@ routing_one_way_meter_punish_fac: 1 # information routing_line_unmatched_punish_fac: 0.5 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + [funicular] # OSM entities to keep on different levels, as k=v. Applies @@ -1324,6 +1344,11 @@ routing_one_way_meter_punish_fac: 1 # information routing_line_unmatched_punish_fac: 0.5 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + [ferry] # OSM entities to keep on different levels, as k=v. Applies @@ -1425,3 +1450,8 @@ routing_one_way_meter_punish_fac: 1 # information routing_line_unmatched_punish_fac: 0.5 +# Punishment factor for every meter a vehicle +# travels through an edge without any line +# information when no specific line was requested +# routing_no_lines_punish_fac: 0 + diff --git a/src/pfaedle/config/MotConfigReader.cpp b/src/pfaedle/config/MotConfigReader.cpp index bffe3a7..96fce0e 100644 --- a/src/pfaedle/config/MotConfigReader.cpp +++ b/src/pfaedle/config/MotConfigReader.cpp @@ -331,6 +331,12 @@ void MotConfigReader::parse(const std::vector& paths) { p.getDouble(secStr, "routing_line_unmatched_punish_fac"); } + if (p.hasKey(secStr, "routing_no_lines_punish_fac")) { + procedKeys.insert("routing_no_lines_punish_fac"); + curCfg.routingOpts.noLinesPunishFact = + p.getDouble(secStr, "routing_no_lines_punish_fac"); + } + if (p.hasKey(secStr, "routing_platform_unmatched_punish")) { procedKeys.insert("routing_platform_unmatched_punish"); curCfg.routingOpts.platformUnmatchedPen = diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index 5cb1862..1c69c40 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -39,6 +39,7 @@ struct RoutingOpts { oneWayPunishFac(1), oneWayEdgePunish(0), lineUnmatchedPunishFact(0.5), + noLinesPunishFact(0), platformUnmatchedPen(0), stationDistPenFactor(0), popReachEdge(true), @@ -49,6 +50,7 @@ struct RoutingOpts { double oneWayPunishFac; double oneWayEdgePunish; double lineUnmatchedPunishFact; + double noLinesPunishFact; double platformUnmatchedPen; double stationDistPenFactor; double nonOsmPen; @@ -65,6 +67,7 @@ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { fabs(a.oneWayPunishFac - b.oneWayPunishFac) < 0.01 && fabs(a.oneWayEdgePunish - b.oneWayEdgePunish) < 0.01 && fabs(a.lineUnmatchedPunishFact - b.lineUnmatchedPunishFact) < 0.01 && + fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.01 && fabs(a.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 && fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 && fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 && @@ -86,7 +89,7 @@ struct EdgeCost { 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) { + double noLinesMeters, double reachPen, const RoutingOpts* o) { if (!o) { _cost = mDist + reachPen; } else { @@ -97,6 +100,7 @@ struct EdgeCost { oneWayMeters * o->oneWayPunishFac + oneWayEdges * o->oneWayEdgePunish + lineUnmatchedMeters * o->lineUnmatchedPunishFact + + noLinesMeters * o->noLinesPunishFact + fullTurns * o->fullTurnPunishFac + passThru * o->passThruStationsPunish + reachPen; } diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp index 38e49c7..c2e0055 100644 --- a/src/pfaedle/router/Router.cpp +++ b/src/pfaedle/router/Router.cpp @@ -60,7 +60,7 @@ EdgeCost NCostFunc::operator()(const trgraph::Node* from, e->pl().lvl() == 5 ? e->pl().getLength() : 0, e->pl().lvl() == 6 ? e->pl().getLength() : 0, e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip, - e->pl().getLength() * oneway, oneway, 0, 0, &_rOpts); + e->pl().getLength() * oneway, oneway, 0, 0, 0, &_rOpts); } // _____________________________________________________________________________ @@ -92,6 +92,8 @@ EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n, } double transitLinePen = transitLineCmp(from->pl()); + bool noLines = (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && + _rAttrs.fromString.empty() && from->pl().getLines().empty()); return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0, from->pl().lvl() == 1 ? from->pl().getLength() : 0, @@ -102,7 +104,8 @@ EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n, from->pl().lvl() == 6 ? from->pl().getLength() : 0, from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns, stationSkip, from->pl().getLength() * oneway, oneway, - from->pl().getLength() * transitLinePen, 0, &_rOpts); + from->pl().getLength() * transitLinePen, + noLines ? from->pl().getLength() : 0, 0, &_rOpts); } // _____________________________________________________________________________ @@ -173,7 +176,7 @@ EdgeCost DistHeur::operator()(const trgraph::Edge* a, double cur = webMercMeterDist(*a->getFrom()->pl().getGeom(), _center) * _rOpts.levelPunish[_lvl]; - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } // _____________________________________________________________________________ @@ -182,7 +185,7 @@ EdgeCost NDistHeur::operator()(const trgraph::Node* a, UNUSED(b); double cur = webMercMeterDist(*a->pl().getGeom(), _center); - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } // _____________________________________________________________________________ @@ -407,7 +410,7 @@ EdgeListHops Router::route(const EdgeCandRoute& route, nodes[e] = cgraph->addNd(route[0][i].e->getFrom()); cgraph->addEdg(source, nodes[e]) ->pl() - .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, route[0][i].pen, 0)); } @@ -474,7 +477,7 @@ EdgeListHops Router::route(const EdgeCandRoute& route, << TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)"; for (auto& kv : edges) { kv.second->pl().setCost( - EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) + + EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) + costs[kv.first]); if (rOpts.popReachEdge && kv.second->pl().getEdges()->size()) { diff --git a/src/pfaedle/router/Router.h b/src/pfaedle/router/Router.h index cdd72f4..74bf581 100644 --- a/src/pfaedle/router/Router.h +++ b/src/pfaedle/router/Router.h @@ -54,7 +54,7 @@ struct CostFunc _rOpts(rOpts), _res(res), _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {} + _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {} const RoutingAttrs& _rAttrs; const RoutingOpts& _rOpts; @@ -77,7 +77,7 @@ struct NCostFunc _rOpts(rOpts), _res(res), _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, std::numeric_limits::infinity(), 0) {} const RoutingAttrs& _rAttrs; From b0a2cff43ac08b40d50a48723cdb96954bc28016 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 20 Nov 2020 16:51:04 +0100 Subject: [PATCH 039/182] fix possible race condition in Normalizer cache --- src/pfaedle/osm/OsmBuilder.cpp | 85 ++++++++++++++++------------- src/pfaedle/router/ShapeBuilder.cpp | 49 +++++++++-------- src/pfaedle/trgraph/Normalizer.cpp | 31 ++++++++++- src/pfaedle/trgraph/Normalizer.h | 16 +++++- src/pfaedle/trgraph/StatGroup.cpp | 2 +- 5 files changed, 117 insertions(+), 66 deletions(-) diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index aae4ee3..3da48df 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -24,28 +24,28 @@ #include "util/log/Log.h" #include "xml/pfxml.h" -using util::geo::webMercMeterDist; -using util::geo::Box; -using util::Nullable; -using pfaedle::trgraph::Normalizer; +using ad::cppgtfs::gtfs::Stop; +using pfaedle::osm::BlockSearch; +using pfaedle::osm::EdgeGrid; +using pfaedle::osm::EqSearch; +using pfaedle::osm::NodeGrid; +using pfaedle::osm::OsmBuilder; +using pfaedle::osm::OsmNode; +using pfaedle::osm::OsmRel; +using pfaedle::osm::OsmWay; +using pfaedle::trgraph::Component; +using pfaedle::trgraph::Edge; +using pfaedle::trgraph::EdgePL; using pfaedle::trgraph::Graph; using pfaedle::trgraph::Node; using pfaedle::trgraph::NodePL; -using pfaedle::trgraph::Edge; -using pfaedle::trgraph::EdgePL; -using pfaedle::trgraph::TransitEdgeLine; -using pfaedle::trgraph::StatInfo; +using pfaedle::trgraph::Normalizer; using pfaedle::trgraph::StatGroup; -using pfaedle::trgraph::Component; -using pfaedle::osm::OsmBuilder; -using pfaedle::osm::OsmWay; -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; +using pfaedle::trgraph::StatInfo; +using pfaedle::trgraph::TransitEdgeLine; +using util::Nullable; +using util::geo::Box; +using util::geo::webMercMeterDist; // _____________________________________________________________________________ bool EqSearch::operator()(const Node* cand, const StatInfo* si) const { @@ -342,7 +342,7 @@ void OsmBuilder::readWriteRels(pfxml::file* i, util::xml::XmlWriter* o, OsmRel rel; while ((rel = nextRel(i, filter, keepAttrs)).id) { OsmIdList realNodes, realWays; - std::vector realNodeRoles, realWayRoles; + std::vector realNodeRoles, realWayRoles; for (size_t j = 0; j < rel.ways.size(); j++) { osmid wid = rel.ways[j]; @@ -425,8 +425,8 @@ void OsmBuilder::readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { NodePL ret( util::geo::latLngToWebMerc(s->getLat(), s->getLng()), - StatInfo(ops.statNormzer(s->getName()), - ops.trackNormzer(s->getPlatformCode()), false)); + StatInfo(ops.statNormzer.norm(s->getName()), + ops.trackNormzer.norm(s->getPlatformCode()), false)); #ifdef PFAEDLE_STATION_IDS // debug feature, store station id from GTFS @@ -434,7 +434,8 @@ NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { #endif if (s->getParentStation()) { - ret.getSI()->addAltName(ops.statNormzer(s->getParentStation()->getName())); + ret.getSI()->addAltName( + ops.statNormzer.norm(s->getParentStation()->getName())); } return ret; @@ -442,9 +443,9 @@ NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { // _____________________________________________________________________________ pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, - OsmIdSet* nohupNodes, - const OsmFilter& filter, - const BBoxIdx& bbox) const { + OsmIdSet* nohupNodes, + const OsmFilter& filter, + const BBoxIdx& bbox) const { bool inNodeBlock = false; uint64_t curId = 0; @@ -739,7 +740,8 @@ void OsmBuilder::readWriteNds(pfxml::file* i, util::xml::XmlWriter* o, {"lat", std::to_string(nd.lat)}, {"lon", std::to_string(nd.lng)}}); for (const auto& kv : nd.attrs) { - o->openTag("tag", {{"k", kv.first}, {"v", pfxml::file::decode(kv.second)}}); + o->openTag("tag", + {{"k", kv.first}, {"v", pfxml::file::decode(kv.second)}}); o->closeTag(); } o->closeTag(); @@ -937,10 +939,11 @@ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, const AttrMap& attrs, const RelMap& entRels, const RelLst& rels, - const Normalizer& norm) const { + const Normalizer& normzer) const { std::string ret; for (const auto& s : rule) { - ret = norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + ret = + normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); if (!ret.empty()) return ret; } @@ -950,11 +953,12 @@ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, // _____________________________________________________________________________ std::vector OsmBuilder::getAttrMatchRanked( const DeepAttrLst& rule, osmid id, const AttrMap& attrs, - const RelMap& entRels, const RelLst& rels, const Normalizer& norm) const { + const RelMap& entRels, const RelLst& rels, + const Normalizer& normzer) const { std::vector ret; for (const auto& s : rule) { std::string tmp = - norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); if (!tmp.empty()) ret.push_back(tmp); } @@ -1183,9 +1187,8 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, int fullTurn = 0; - if (cur.fromEdge && - cur.node->getInDeg() + cur.node->getOutDeg() > - 2) { // only intersection angles + if (cur.fromEdge && cur.node->getInDeg() + cur.node->getOutDeg() > + 2) { // only intersection angles const POINT& toP = *cand->pl().getGeom(); const POINT& fromP = *cur.fromEdge->getOtherNd(cur.node)->pl().getGeom(); @@ -1425,7 +1428,8 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.sNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.shortName = ops.lineNormzer(pfxml::file::decode(relAttr.second)); + el.shortName = + ops.lineNormzer.norm(pfxml::file::decode(relAttr.second)); if (!el.shortName.empty()) found = true; } } @@ -1436,7 +1440,8 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.fromNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.fromStr = ops.statNormzer(pfxml::file::decode(relAttr.second)); + el.fromStr = + ops.statNormzer.norm(pfxml::file::decode(relAttr.second)); if (!el.fromStr.empty()) found = true; } } @@ -1447,7 +1452,8 @@ std::vector OsmBuilder::getLines( for (const auto& r : ops.relLinerules.toNameRule) { for (const auto& relAttr : rels.rels[relId]) { if (relAttr.first == r) { - el.toStr = ops.statNormzer(pfxml::file::decode(relAttr.second)); + el.toStr = + ops.statNormzer.norm(pfxml::file::decode(relAttr.second)); if (!el.toStr.empty()) found = true; } } @@ -1895,9 +1901,10 @@ void OsmBuilder::snapStats(const OsmReadOpts& opts, Graph* g, } if (notSnapped.size()) - LOG(VDEBUG) << notSnapped.size() << " stations could not be snapped in " - "normal run, trying again in orphan " - "mode."; + 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 diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index 898b5c4..cd5c85b 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -29,28 +29,28 @@ #include "util/graph/EDijkstra.h" #include "util/log/Log.h" -using util::geo::extendBox; using util::geo::DBox; using util::geo::DPoint; +using util::geo::extendBox; using util::geo::minbox; -using util::geo::webMercMeterDist; -using util::geo::webMercToLatLng; -using util::geo::latLngToWebMerc; -using util::geo::output::GeoGraphJsonOutput; -using pfaedle::router::ShapeBuilder; +using ad::cppgtfs::gtfs::ShapePoint; +using ad::cppgtfs::gtfs::Stop; +using pfaedle::gtfs::Feed; +using pfaedle::gtfs::StopTime; +using pfaedle::gtfs::Trip; +using pfaedle::osm::BBoxIdx; +using pfaedle::router::Clusters; +using pfaedle::router::EdgeListHops; using pfaedle::router::FeedStops; using pfaedle::router::NodeCandGroup; using pfaedle::router::NodeCandRoute; using pfaedle::router::RoutingAttrs; -using pfaedle::router::EdgeListHops; -using pfaedle::router::Clusters; -using pfaedle::osm::BBoxIdx; -using ad::cppgtfs::gtfs::Stop; -using pfaedle::gtfs::Trip; -using pfaedle::gtfs::Feed; -using pfaedle::gtfs::StopTime; -using ad::cppgtfs::gtfs::ShapePoint; +using pfaedle::router::ShapeBuilder; +using util::geo::latLngToWebMerc; +using util::geo::webMercMeterDist; +using util::geo::webMercToLatLng; +using util::geo::output::GeoGraphJsonOutput; // _____________________________________________________________________________ ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, @@ -404,16 +404,17 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) { const auto& lnormzer = _motCfg.osmBuildOpts.lineNormzer; - ret.shortName = lnormzer(trip->getRoute()->getShortName()); - - if (ret.shortName.empty()) ret.shortName = lnormzer(trip->getShortname()); + ret.shortName = lnormzer.norm(trip->getRoute()->getShortName()); if (ret.shortName.empty()) - ret.shortName = lnormzer(trip->getRoute()->getLongName()); + ret.shortName = lnormzer.norm(trip->getShortname()); - ret.fromString = _motCfg.osmBuildOpts.statNormzer( + if (ret.shortName.empty()) + ret.shortName = lnormzer.norm(trip->getRoute()->getLongName()); + + ret.fromString = _motCfg.osmBuildOpts.statNormzer.norm( trip->getStopTimes().begin()->getStop()->getName()); - ret.toString = _motCfg.osmBuildOpts.statNormzer( + ret.toString = _motCfg.osmBuildOpts.statNormzer.norm( (--trip->getStopTimes().end())->getStop()->getName()); return _rAttrs @@ -532,12 +533,12 @@ Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { bool ShapeBuilder::routingEqual(const Stop* a, const Stop* b) { if (a == b) return true; // trivial - auto namea = _motCfg.osmBuildOpts.statNormzer(a->getName()); - auto nameb = _motCfg.osmBuildOpts.statNormzer(b->getName()); + auto namea = _motCfg.osmBuildOpts.statNormzer.norm(a->getName()); + auto nameb = _motCfg.osmBuildOpts.statNormzer.norm(b->getName()); if (namea != nameb) return false; - auto tracka = _motCfg.osmBuildOpts.trackNormzer(a->getPlatformCode()); - auto trackb = _motCfg.osmBuildOpts.trackNormzer(b->getPlatformCode()); + auto tracka = _motCfg.osmBuildOpts.trackNormzer.norm(a->getPlatformCode()); + auto trackb = _motCfg.osmBuildOpts.trackNormzer.norm(b->getPlatformCode()); if (tracka != trackb) return false; POINT ap = diff --git a/src/pfaedle/trgraph/Normalizer.cpp b/src/pfaedle/trgraph/Normalizer.cpp index 173444e..5e254a0 100644 --- a/src/pfaedle/trgraph/Normalizer.cpp +++ b/src/pfaedle/trgraph/Normalizer.cpp @@ -3,7 +3,9 @@ // Authors: Patrick Brosi #include +#include #include +#include #include #include #include @@ -15,12 +17,39 @@ using pfaedle::trgraph::Normalizer; // _____________________________________________________________________________ -Normalizer::Normalizer(const ReplRules& rules) : _rulesOrig(rules) { +Normalizer::Normalizer(const ReplRules& rules) + : _rulesOrig(rules) { buildRules(rules); } +// _____________________________________________________________________________ +Normalizer::Normalizer(const Normalizer& other) + : _rules(other._rules), + _rulesOrig(other._rulesOrig), + _cache(other._cache) {} + +// _____________________________________________________________________________ +Normalizer& Normalizer::operator=(Normalizer other) { + std::swap(this->_rules, other._rules); + std::swap(this->_rulesOrig, other._rulesOrig); + std::swap(this->_cache, other._cache); + + return *this; +} + // _____________________________________________________________________________ std::string Normalizer::operator()(std::string sn) const { + return normTS(sn); +} + +// _____________________________________________________________________________ +std::string Normalizer::normTS(const std::string& sn) const { + std::lock_guard lock(_mutex); + return norm(sn); +} + +// _____________________________________________________________________________ +std::string Normalizer::norm(const std::string& sn) const { auto i = _cache.find(sn); if (i != _cache.end()) return i->second; diff --git a/src/pfaedle/trgraph/Normalizer.h b/src/pfaedle/trgraph/Normalizer.h index d85aac2..6546963 100644 --- a/src/pfaedle/trgraph/Normalizer.h +++ b/src/pfaedle/trgraph/Normalizer.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace pfaedle { namespace trgraph { @@ -28,7 +29,19 @@ class Normalizer { Normalizer() {} explicit Normalizer(const ReplRules& rules); - // Normalize sn based on the rules of this normalizer + // copy constructor + Normalizer(const Normalizer& other); + + // assignment op + Normalizer& operator=(Normalizer other); + + // Normalize sn, not thread safe + std::string norm(const std::string& sn) const; + // Normalize sn, thread safe + std::string normTS(const std::string& sn) const; + + // Normalize sn based on the rules of this normalizer, uses the thread safe + // version of norm() internally std::string operator()(std::string sn) const; bool operator==(const Normalizer& b) const; @@ -36,6 +49,7 @@ class Normalizer { ReplRulesComp _rules; ReplRules _rulesOrig; mutable std::unordered_map _cache; + mutable std::mutex _mutex; void buildRules(const ReplRules& rules); }; diff --git a/src/pfaedle/trgraph/StatGroup.cpp b/src/pfaedle/trgraph/StatGroup.cpp index 67c5142..75b45d4 100644 --- a/src/pfaedle/trgraph/StatGroup.cpp +++ b/src/pfaedle/trgraph/StatGroup.cpp @@ -68,7 +68,7 @@ double StatGroup::getPen(const Stop* s, trgraph::Node* n, double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom()); distPen *= distPenFac; - std::string platform = platformNorm(s->getPlatformCode()); + std::string platform = platformNorm.norm(s->getPlatformCode()); if (!platform.empty() && !n->pl().getSI()->getTrack().empty() && n->pl().getSI()->getTrack() == platform) { From 2a01eda564bef06bf7142d062b7e5b57559774e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 2 Dec 2020 13:53:29 +0100 Subject: [PATCH 040/182] add Dockerfile & .dockerignore --- .dockerignore | 3 +++ Dockerfile | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7904451 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +/build + +/Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5012f35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM debian:buster-slim AS builder + +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y g++ cmake git + +ADD . /app +RUN mkdir build && \ + cd build && \ + cmake .. && \ + make -j && \ + pwd && \ + make install + +FROM debian:buster-slim + +RUN apt-get update && \ + apt-get install -y libgomp1 && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=builder /usr/local/etc/pfaedle /usr/local/etc/pfaedle +COPY --from=builder /usr/local/bin/pfaedle /usr/local/bin/pfaedle + +ENTRYPOINT ["/usr/local/bin/pfaedle"] From 4aa500b867b75d846b5861600f4293646c4fb138 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 2 Dec 2020 13:53:50 +0100 Subject: [PATCH 041/182] =?UTF-8?q?Travis=20CI:=20build=20&=20publish=20Do?= =?UTF-8?q?cker=20image=20=F0=9F=92=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 + .travis.yml | 7 +++++++ push-docker-image.sh | 6 ++++++ 3 files changed, 14 insertions(+) create mode 100644 push-docker-image.sh diff --git a/.dockerignore b/.dockerignore index 7904451..9ea4a81 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ /build /Dockerfile +/push-docker-image.sh diff --git a/.travis.yml b/.travis.yml index 81a0719..85eb9f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,13 @@ before_script: script: - make -j4 - ctest --verbose + - docker build -t ad-freiburg/pfaedle . + +deploy: + on: + branch: master + provider: script + script: bash push-docker-image.sh notifications: email: diff --git a/push-docker-image.sh b/push-docker-image.sh new file mode 100644 index 0000000..902bf39 --- /dev/null +++ b/push-docker-image.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -ex +set -o pipefail + +echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin +docker push ad-freiburg/pfaedle \ No newline at end of file From 9a8a64d38ee075641d64be742f28fc433bb6181b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 2 Dec 2020 17:53:31 +0100 Subject: [PATCH 042/182] =?UTF-8?q?readme:=20explain=20docker=20usage=20?= =?UTF-8?q?=F0=9F=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index c21c924..a860d35 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,20 @@ input GTFS feed and the input configuration. This can be used to avoid parsing (for example) the entire `planet.osm` on each run. +## via Docker + +You can use the [`ad-freiburg/pfaedle` Docker image](https://hub.docker.com/repository/docker/ad-freiburg/pfaedle) by mounting the OSM & GTFS data into the container: + +```shell +docker run -i --rm \ + # mount OSM data + --volume /path/to/osm/data:/osm \ + # mount GTFS data + --volume /path/to/gtfs/data:/gtfs \ + # tell pfaedle where to find the data + pfaedle -x /osm/osm-data.xml -i /gtfs +``` + ## Debugging The following flags may be useful for debugging: From b9897bdd97a103d2960e3505c512b7c1751f6ae7 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Wed, 2 Dec 2020 19:26:40 +0100 Subject: [PATCH 043/182] add docker pw and uname --- .travis.yml | 39 ++++++++++++++++++--------------------- push-docker-image.sh | 2 +- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85eb9f0..9087d87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,37 @@ language: cpp - os: - - linux - - osx - +- linux +- osx compiler: - - gcc - - clang - +- gcc +- clang addons: apt: sources: - - ubuntu-toolchain-r-test + - ubuntu-toolchain-r-test packages: - - cmake - + - cmake before_install: - - export LD_LIBRARY_PATH=$(if [[ $CXX == "clang++" ]]; then echo -n '/usr/local/clang/lib'; fi) - +- export LD_LIBRARY_PATH=$(if [[ $CXX == "clang++" ]]; then echo -n '/usr/local/clang/lib'; + fi) before_script: - - mkdir build - - cd build - - cmake .. - +- mkdir build +- cd build +- cmake .. script: - - make -j4 - - ctest --verbose - - docker build -t ad-freiburg/pfaedle . - +- make -j4 +- ctest --verbose +- docker build -t ad-freiburg/pfaedle . deploy: on: branch: master provider: script script: bash push-docker-image.sh - notifications: email: on_success: never on_failure: always +env: + global: + - secure: W60bRWv9u28UFjmt1iO5ELtPBvUNkAPqiwwxd+boy7BazAJ0fUUBZoEpnsgv8pqKWJV6VMUvknP4taU5a6NM+3aRHuCZOjVC42Rs8oDGJoXrhmH9ZzOOp2nDnHy9hqrtRKJrYQUww+s7UjBpWcaorqHvo5iWNrt9OulKM+V2u6IQI3xI1bPoaVhK/EnHHFAWe52v0KOkaSjguL5zj7xZqCeaZKmX9PsiQdqQJVtX2zsdF/aDkDvhkAl4SxeVKrFEVDV4gPx7yqGC/uQ6YJrQXigqpWWL6oZ1cxsg2HWqLZyAYN8tIWcnaAW8+PVYLfH1iTDb6fnokD4DPpVfULz4dzqOnTuG+Qd97U2BVDJN+LdxK2d8RZ1KLAWNbFGBlkY/8zpMAtV/xhGk2vHg/pj6ZPUPncNlOzUepASw5yCY7H6SOH1NgzNNSn+Fg3Q+6eUoYWp5jrpejBcwO/tikRCfGOSNyEKTC+1joRNwySeLjTcLDcaO3+EJL64YjIKW1+YwVFopq5DKRhzjSyO+dUryA5+l+nT499BC9dxA7SvQ/tLwMs3uPlVhSDUvkH6DxiWIJQEYmTZIEA4JmLjdBFDB9FaApubvn5wRIlL07Dbq+t3XxMciWeLU3H2IQzlGPQpIbqB93L8yc6eH5Fq2Gu4HN5lmpJC3ZLpZOEqNk1kcFzo= + - secure: b10JKrMacKD+C8yGHPiOYP84ykXqBFiwm2wkcKn9SkGO6bDCtRIM+/eYMN64wQVD54Vwc/rfxqCRN6ckdAgw1zrtBdlRLz1krdwsdb7RuXJvTTTu3bd8CrtlsvcVh40vcItcFAQQKhKrPF/iDajbXm4GaVcoiqV8i8inhhfg063guC22o3D76J3xFmwyhNGv0QZuK7xG0O2h+mflU/LE8FuXrVO4+1QmvwJ9JRgBnz5F8jrEuZWipp3gJBVnpYHv4ZAy5r52zQ3iPEkji7Y6/GjvxYnjc08QM998I3SSlUuW4quiEPJFTGxK9w/UV34c0DJhzluJ3TGTz+RkhejIDUcNiKqFKWZCzCwzcx5f96RmTh7MTHulB7zTkK0hzSSPFjrYrRrkN/FCwMrHaLs1H3SQbXiB2Ga2pnfaOVfCbM5KraZHlk2xHUIqVHkhyetETBW76d4g6vxjNoe++siyx+eUW2VMj7Y+6c1HQPceGi+jpl9pJK6ZXKfVpfWjFj29qqnv7lNjoI3PXGllswDV4KxP/A5A4MBqHAcVOFEdTro5EReUhepyNYM7lUaS//Wa6DzE6US13bVpIr4fC+bKUf8XizfGp+f9TSc+Shl0J6asejxIbgQRCopTJd805xAFoCsoK1yt/sZIplBO/mjaBjLc9Y1+A04VH5FQWl92rHQ= diff --git a/push-docker-image.sh b/push-docker-image.sh index 902bf39..af6f3fb 100644 --- a/push-docker-image.sh +++ b/push-docker-image.sh @@ -3,4 +3,4 @@ set -ex set -o pipefail echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin -docker push ad-freiburg/pfaedle \ No newline at end of file +docker push adfreiburg/pfaedle From 2f7e6a028ffd4c19ae94267e5f016dabeb6b5987 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Wed, 2 Dec 2020 19:29:30 +0100 Subject: [PATCH 044/182] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a860d35..064d826 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ run. ## via Docker -You can use the [`ad-freiburg/pfaedle` Docker image](https://hub.docker.com/repository/docker/ad-freiburg/pfaedle) by mounting the OSM & GTFS data into the container: +You can use the [`adfreiburg/pfaedle` Docker image](https://hub.docker.com/repository/docker/adfreiburg/pfaedle) by mounting the OSM & GTFS data into the container: ```shell docker run -i --rm \ From 8fe7a30e312bcadcbf05b927d895690f9c3436eb Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 3 Dec 2020 00:05:34 +0100 Subject: [PATCH 045/182] add docker service to travis.yml --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9087d87..f1863eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ addons: - ubuntu-toolchain-r-test packages: - cmake +services: +- docker before_install: - export LD_LIBRARY_PATH=$(if [[ $CXX == "clang++" ]]; then echo -n '/usr/local/clang/lib'; fi) @@ -21,7 +23,7 @@ before_script: script: - make -j4 - ctest --verbose -- docker build -t ad-freiburg/pfaedle . +- docker build -t adfreiburg/pfaedle . deploy: on: branch: master From dd64e075c8d0335e61b2c2ccfb36270a6e811799 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 3 Dec 2020 01:38:08 +0100 Subject: [PATCH 046/182] only deploy on linux using gcc --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f1863eb..b1f8f80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,8 @@ script: deploy: on: branch: master + condition: "$CC = gcc" + os: linux provider: script script: bash push-docker-image.sh notifications: From 73101fcc03ddbcfcd4822811597f0bc9ccb92f8a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 3 Dec 2020 12:50:17 +0100 Subject: [PATCH 047/182] only build image on linux --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b1f8f80..eecaaaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ before_script: script: - make -j4 - ctest --verbose -- docker build -t adfreiburg/pfaedle . +- if [[ ( "$TRAVIS_OS_NAME" == "linux" ) ]]; then cd .. && docker build -t adfreiburg/pfaedle .; fi deploy: on: branch: master From 087ea0310b7f51a5707910704de725a73f0a2629 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 3 Dec 2020 14:44:14 +0100 Subject: [PATCH 048/182] move os condition to condition clause --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eecaaaf..2f5f65a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,8 +27,7 @@ script: deploy: on: branch: master - condition: "$CC = gcc" - os: linux + condition: "$CC = gcc && $TRAVIS_OS_NAME = linux" provider: script script: bash push-docker-image.sh notifications: From e0e4ee1f2b1a58128cab9db77e7895ba749dc8f4 Mon Sep 17 00:00:00 2001 From: graue70 <23035329+graue70@users.noreply.github.com> Date: Sun, 27 Jun 2021 12:20:15 +0200 Subject: [PATCH 049/182] Fix incorrect URL in README (#30) Old URL forced login. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 064d826..2694004 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ run. ## via Docker -You can use the [`adfreiburg/pfaedle` Docker image](https://hub.docker.com/repository/docker/adfreiburg/pfaedle) by mounting the OSM & GTFS data into the container: +You can use the [`adfreiburg/pfaedle` Docker image](https://hub.docker.com/r/adfreiburg/pfaedle) by mounting the OSM & GTFS data into the container: ```shell docker run -i --rm \ From cea4474631814a126ec1cb5bba8a049e55c50c3a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sat, 24 Jul 2021 10:06:19 +0200 Subject: [PATCH 050/182] write doubles as bytes to shape container, not strings, to safe some disk space and speed up writing --- src/pfaedle/gtfs/ShapeContainer.h | 1 + src/pfaedle/gtfs/ShapeContainer.tpp | 39 ++++++++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/pfaedle/gtfs/ShapeContainer.h b/src/pfaedle/gtfs/ShapeContainer.h index 1128031..be8a9b5 100644 --- a/src/pfaedle/gtfs/ShapeContainer.h +++ b/src/pfaedle/gtfs/ShapeContainer.h @@ -61,6 +61,7 @@ class ShapeContainer { size_t _max; std::string _curId; std::stringstream _writeBuffer; + std::fpos _lastBuff; }; #include "ShapeContainer.tpp" diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index b55d646..1b5c196 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -3,10 +3,11 @@ // Authors: Patrick Brosi #include +#include // ____________________________________________________________________________ template -ShapeContainer::ShapeContainer() { +ShapeContainer::ShapeContainer() : _lastBuff(0) { std::string f = pfaedle::getTmpFName("", ""); _storage.open(f, std::fstream::in | std::fstream::out | std::fstream::trunc); @@ -23,6 +24,8 @@ ShapeContainer::ShapeContainer() { // ____________________________________________________________________________ template ShapeContainer::~ShapeContainer() { + _storage << _writeBuffer.rdbuf(); + _storage.flush(); _storage.close(); } @@ -70,18 +73,22 @@ size_t ShapeContainer::size() const { template std::string ShapeContainer::add(const ad::cppgtfs::gtfs::Shape& s) { if (has(s.getId())) return s.getId(); + size_t size = s.getPoints().size(); _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'; + _writeBuffer << s.getId(); + _writeBuffer.put(' '); + _writeBuffer.write(reinterpret_cast(&size), sizeof(size)); - if (_writeBuffer.tellp() > 1000 * 5000) { - _storage << _writeBuffer.rdbuf(); + for (const auto& p : s.getPoints()) { + _writeBuffer.write(reinterpret_cast(&p.lat), sizeof(p.lat)); + _writeBuffer.write(reinterpret_cast(&p.lng), sizeof(p.lng)); + _writeBuffer.write(reinterpret_cast(&p.travelDist), sizeof(p.travelDist)); + } + + if (_writeBuffer.tellp() - _lastBuff > 1000 * 5000) { + _lastBuff = _writeBuffer.tellp(); + _storage << _writeBuffer.rdbuf(); _writeBuffer.clear(); } @@ -92,6 +99,7 @@ std::string ShapeContainer::add(const ad::cppgtfs::gtfs::Shape& s) { template void ShapeContainer::open() { _storage << _writeBuffer.rdbuf(); + _storage.flush(); _writeBuffer.clear(); _ptr = 0; @@ -107,14 +115,17 @@ bool ShapeContainer::nextStoragePt( while (_storage.good() && !_storage.fail()) { if (!_ptr) { _storage >> _curId; - _storage >> _max; + _storage.ignore(); + + _storage.read(reinterpret_cast(&_max), sizeof(_max)); } if (!_storage.good() || _storage.fail()) return false; - _storage >> ret->lat; - _storage >> ret->lng; - _storage >> ret->travelDist; + _storage.read(reinterpret_cast(&ret->lat), sizeof(ret->lat)); + _storage.read(reinterpret_cast(&ret->lng), sizeof(ret->lng)); + _storage.read(reinterpret_cast(&ret->travelDist), sizeof(ret->travelDist)); + ret->seq = _ptr + 1; ret->id = _curId; From deaaf23cafac199d2f110dcc4876e557b524be17 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sat, 24 Jul 2021 10:07:27 +0200 Subject: [PATCH 051/182] linter --- src/pfaedle/gtfs/ShapeContainer.tpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index 1b5c196..60f2b61 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -2,8 +2,8 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi -#include #include +#include // ____________________________________________________________________________ template @@ -15,8 +15,7 @@ ShapeContainer::ShapeContainer() : _lastBuff(0) { unlink(f.c_str()); if (!_storage.good()) { - std::cerr << "Could not open temporary file " << f - << std::endl; + std::cerr << "Could not open temporary file " << f << std::endl; exit(1); } } @@ -83,12 +82,13 @@ std::string ShapeContainer::add(const ad::cppgtfs::gtfs::Shape& s) { for (const auto& p : s.getPoints()) { _writeBuffer.write(reinterpret_cast(&p.lat), sizeof(p.lat)); _writeBuffer.write(reinterpret_cast(&p.lng), sizeof(p.lng)); - _writeBuffer.write(reinterpret_cast(&p.travelDist), sizeof(p.travelDist)); + _writeBuffer.write(reinterpret_cast(&p.travelDist), + sizeof(p.travelDist)); } if (_writeBuffer.tellp() - _lastBuff > 1000 * 5000) { - _lastBuff = _writeBuffer.tellp(); - _storage << _writeBuffer.rdbuf(); + _lastBuff = _writeBuffer.tellp(); + _storage << _writeBuffer.rdbuf(); _writeBuffer.clear(); } @@ -124,7 +124,8 @@ bool ShapeContainer::nextStoragePt( _storage.read(reinterpret_cast(&ret->lat), sizeof(ret->lat)); _storage.read(reinterpret_cast(&ret->lng), sizeof(ret->lng)); - _storage.read(reinterpret_cast(&ret->travelDist), sizeof(ret->travelDist)); + _storage.read(reinterpret_cast(&ret->travelDist), + sizeof(ret->travelDist)); ret->seq = _ptr + 1; ret->id = _curId; From 366e4b2abaf8925a3949a0905ed264ae2c026143 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Wed, 8 Sep 2021 21:21:44 +0200 Subject: [PATCH 052/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index c704a61..1175010 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit c704a610d91ec2be575c42a760cc6157c8fdf932 +Subproject commit 1175010c872f177dc83dc4100e53fcb102961e8b From 16e66e59a52d6b38490b75cb59adcc1fd7056640 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Thu, 9 Sep 2021 00:14:52 +0200 Subject: [PATCH 053/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index 1175010..9d6be1c 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 1175010c872f177dc83dc4100e53fcb102961e8b +Subproject commit 9d6be1c8720da3c72d151400ab86c646089d9484 From cfc41d01e7237070d04079856b114f393f27973c Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 10 Sep 2021 12:36:19 +0200 Subject: [PATCH 054/182] remove check which was alywas true --- src/util/geo/Grid.tpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/geo/Grid.tpp b/src/util/geo/Grid.tpp index 144a69d..9e01098 100644 --- a/src/util/geo/Grid.tpp +++ b/src/util/geo/Grid.tpp @@ -89,8 +89,8 @@ void Grid::get(const Box& box, std::set* s) const { size_t neX = getCellXFromX(box.getUpperRight().getX()); size_t neY = getCellYFromY(box.getUpperRight().getY()); - for (size_t x = swX; x <= neX && x >= 0 && x < _xWidth; x++) - for (size_t y = swY; y <= neY && y >= 0 && y < _yHeight; y++) get(x, y, s); + for (size_t x = swX; x <= neX && x < _xWidth; x++) + for (size_t y = swY; y <= neY && y < _yHeight; y++) get(x, y, s); } // _____________________________________________________________________________ From 364d6a19e5f7b148d4369a2b24238f82072a2f24 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 10 Sep 2021 12:53:28 +0200 Subject: [PATCH 055/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index 9d6be1c..d444e71 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 9d6be1c8720da3c72d151400ab86c646089d9484 +Subproject commit d444e7185f783e0434386eef3b2d996ab246264b From 59e527bcb444eaf6d5fcf938b2204227970f3f53 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 9 Nov 2021 12:01:49 +0100 Subject: [PATCH 056/182] update repo URL --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 826a1a6..0b9e413 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "src/cppgtfs"] path = src/cppgtfs - url = https://ad-git.informatik.uni-freiburg.de/ad/cppgtfs.git + url = https://github.com/ad-freiburg/cppgtfs.git [submodule "src/xml"] path = src/xml url = https://github.com/patrickbr/pfxml.git From 57de18844f68169bdd94cddd9488bdccffa63c7a Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 30 Nov 2021 12:45:58 +0100 Subject: [PATCH 057/182] update cppgtfs --- src/cppgtfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppgtfs b/src/cppgtfs index d444e71..be682b2 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit d444e7185f783e0434386eef3b2d996ab246264b +Subproject commit be682b2cc13125147bad9ebe544f0bad25d0bd22 From f1822868c5addbf169ec19ec84b6db90a198db6f Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Tue, 30 Nov 2021 12:46:24 +0100 Subject: [PATCH 058/182] include cstddef in Node.h --- src/util/graph/Node.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/graph/Node.h b/src/util/graph/Node.h index 62d5984..f962dc8 100644 --- a/src/util/graph/Node.h +++ b/src/util/graph/Node.h @@ -6,6 +6,7 @@ #define UTIL_GRAPH_NODE_H_ #include +#include namespace util { namespace graph { From 4c29892658fb6b3417b021e402404ce4fbf21f91 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Mon, 3 Jan 2022 22:27:59 +0100 Subject: [PATCH 059/182] * speed up hop-to-hop calculations * better and faster trip clustering: trip tries * add --write-colors to extract line colors from OSM data * refactor config parameter names, update default pfaedle.cfg * add --stats for writing a stats.json file * add --no-fast-hops, --no-a-star, --no-trie for debugging * general refactoring --- CMakeLists.txt | 31 +- README.md | 41 - eval/Makefile | 133 - eval/eval-wo-osm-line-rels.cfg | 987 --- eval/eval.cfg | 1002 --- geo/pfaedle.qgs | 7371 +++++------------ pfaedle.cfg | 514 +- src/CMakeLists.txt | 1 + src/configparser | 2 +- src/cppgtfs | 2 +- src/pfaedle/CMakeLists.txt | 2 + src/pfaedle/Def.h | 16 +- src/pfaedle/PfaedleMain.cpp | 210 +- src/pfaedle/config/ConfigReader.cpp | 96 +- src/pfaedle/config/MotConfig.h | 13 +- src/pfaedle/config/MotConfigReader.cpp | 519 +- src/pfaedle/config/MotConfigReader.h | 2 +- src/pfaedle/config/PfaedleConfig.h | 33 +- src/pfaedle/eval/Collector.cpp | 417 - src/pfaedle/gtfs/Feed.h | 14 +- src/pfaedle/gtfs/Route.h | 61 - src/pfaedle/gtfs/ShapeContainer.tpp | 8 +- src/pfaedle/gtfs/StopTime.h | 12 +- src/pfaedle/gtfs/Writer.cpp | 92 +- src/pfaedle/gtfs/Writer.h | 26 +- src/pfaedle/netgraph/EdgePL.h | 4 +- src/pfaedle/osm/BBoxIdx.cpp | 8 +- src/pfaedle/osm/OsmBuilder.cpp | 855 +- src/pfaedle/osm/OsmBuilder.h | 78 +- src/pfaedle/osm/OsmFilter.cpp | 6 + src/pfaedle/osm/OsmFilter.h | 3 +- src/pfaedle/osm/OsmIdSet.cpp | 63 +- src/pfaedle/osm/OsmIdSet.h | 10 +- src/pfaedle/osm/OsmReadOpts.h | 79 +- src/pfaedle/router/Comp.h | 4 +- src/pfaedle/router/EdgePL.cpp | 88 - src/pfaedle/router/EdgePL.h | 51 - src/pfaedle/router/Graph.h | 26 - src/pfaedle/router/HopCache.cpp | 40 + src/pfaedle/router/HopCache.h | 39 + src/pfaedle/router/Misc.h | 131 +- src/pfaedle/router/NodePL.h | 40 - src/pfaedle/router/Router.cpp | 646 -- src/pfaedle/router/Router.h | 213 +- src/pfaedle/router/Router.tpp | 614 ++ src/pfaedle/router/RoutingAttrs.h | 80 +- src/pfaedle/router/ShapeBuilder.cpp | 1337 ++- src/pfaedle/router/ShapeBuilder.h | 150 +- src/pfaedle/router/Stats.h | 33 + src/pfaedle/router/TripTrie.cpp | 219 + src/pfaedle/router/TripTrie.h | 65 + src/pfaedle/router/Weights.cpp | 261 + src/pfaedle/router/Weights.h | 161 + .../StatsimiClassifier.cpp | 30 + .../statsimi-classifier/StatsimiClassifier.h | 35 + src/pfaedle/tests/CMakeLists.txt | 2 + src/pfaedle/tests/TestMain.cpp | 329 + src/pfaedle/trgraph/EdgePL.cpp | 56 +- src/pfaedle/trgraph/EdgePL.h | 27 +- src/pfaedle/trgraph/Graph.h | 4 +- src/pfaedle/trgraph/NodePL.cpp | 105 +- src/pfaedle/trgraph/NodePL.h | 28 +- src/pfaedle/trgraph/Normalizer.cpp | 12 - src/pfaedle/trgraph/Normalizer.h | 7 - src/pfaedle/trgraph/StatGroup.cpp | 94 - src/pfaedle/trgraph/StatGroup.h | 72 - src/pfaedle/trgraph/StatInfo.cpp | 56 +- src/pfaedle/trgraph/StatInfo.h | 27 +- src/shapevl/CMakeLists.txt | 15 + src/shapevl/Collector.cpp | 373 + src/{pfaedle/eval => shapevl}/Collector.h | 51 +- src/{pfaedle/eval => shapevl}/Result.h | 3 +- src/shapevl/ShapevlMain.cpp | 174 + src/util/3rdparty/MurmurHash3.cpp | 335 + src/util/3rdparty/MurmurHash3.h | 37 + src/util/3rdparty/dtoa_milo.h | 454 + src/util/Misc.h | 312 +- src/util/PriorityQueue.h | 39 + src/util/PriorityQueue.tpp | 41 + src/util/String.h | 106 +- src/util/geo/BezierCurve.tpp | 2 +- src/util/geo/CircularSegment.h | 41 + src/util/geo/CircularSegment.tpp | 51 + src/util/geo/Geo.h | 422 +- src/util/geo/Grid.h | 54 +- src/util/geo/Grid.tpp | 37 +- src/util/geo/PolyLine.h | 5 +- src/util/geo/PolyLine.tpp | 49 +- src/util/geo/Polygon.h | 5 +- src/util/geo/QuadTree.h | 94 + src/util/geo/QuadTree.tpp | 117 + src/util/geo/output/GeoGraphJsonOutput.h | 22 +- src/util/geo/output/GeoGraphJsonOutput.tpp | 23 +- src/util/geo/output/GeoJsonOutput.h | 24 +- src/util/geo/output/GeoJsonOutput.tpp | 65 +- src/util/graph/Algorithm.h | 17 +- src/util/graph/Algorithm.tpp | 12 +- src/util/graph/BiDijkstra.cpp | 7 + src/util/graph/BiDijkstra.h | 129 + src/util/graph/BiDijkstra.tpp | 293 + src/util/graph/Dijkstra.h | 53 +- src/util/graph/Dijkstra.tpp | 74 +- src/util/graph/DirGraph.h | 2 - src/util/graph/DirGraph.tpp | 6 +- src/util/graph/DirNode.tpp | 6 +- src/util/graph/EDijkstra.h | 150 +- src/util/graph/EDijkstra.tpp | 369 +- src/util/graph/Edge.h | 3 - src/util/graph/Graph.h | 7 +- src/util/graph/Graph.tpp | 17 +- src/util/graph/Node.h | 5 +- src/util/graph/ShortestPath.h | 265 +- src/util/graph/UndirGraph.tpp | 2 +- src/util/graph/radix_heap.h | 226 + src/util/graph/robin/robin_growth_policy.h | 348 + src/util/graph/robin/robin_hash.h | 1451 ++++ src/util/graph/robin/robin_map.h | 715 ++ src/util/graph/robin/robin_set.h | 582 ++ src/util/http/Server.cpp | 12 +- src/util/json/Writer.cpp | 11 +- src/util/json/Writer.h | 6 +- src/util/log/Log.h | 10 +- src/util/tests/QuadTreeTest.cpp | 38 + src/util/tests/QuadTreeTest.h | 12 + src/util/tests/TestMain.cpp | 1198 +-- src/xml | 2 +- 126 files changed, 14576 insertions(+), 12196 deletions(-) delete mode 100644 eval/Makefile delete mode 100644 eval/eval-wo-osm-line-rels.cfg delete mode 100644 eval/eval.cfg delete mode 100644 src/pfaedle/eval/Collector.cpp delete mode 100644 src/pfaedle/gtfs/Route.h delete mode 100644 src/pfaedle/router/EdgePL.cpp delete mode 100644 src/pfaedle/router/EdgePL.h delete mode 100644 src/pfaedle/router/Graph.h create mode 100644 src/pfaedle/router/HopCache.cpp create mode 100644 src/pfaedle/router/HopCache.h delete mode 100644 src/pfaedle/router/NodePL.h delete mode 100644 src/pfaedle/router/Router.cpp create mode 100644 src/pfaedle/router/Router.tpp create mode 100644 src/pfaedle/router/Stats.h create mode 100644 src/pfaedle/router/TripTrie.cpp create mode 100644 src/pfaedle/router/TripTrie.h create mode 100644 src/pfaedle/router/Weights.cpp create mode 100644 src/pfaedle/router/Weights.h create mode 100644 src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp create mode 100644 src/pfaedle/statsimi-classifier/StatsimiClassifier.h create mode 100644 src/pfaedle/tests/CMakeLists.txt create mode 100644 src/pfaedle/tests/TestMain.cpp delete mode 100644 src/pfaedle/trgraph/StatGroup.cpp delete mode 100644 src/pfaedle/trgraph/StatGroup.h create mode 100644 src/shapevl/CMakeLists.txt create mode 100644 src/shapevl/Collector.cpp rename src/{pfaedle/eval => shapevl}/Collector.h (64%) rename src/{pfaedle/eval => shapevl}/Result.h (93%) create mode 100644 src/shapevl/ShapevlMain.cpp create mode 100644 src/util/3rdparty/MurmurHash3.cpp create mode 100644 src/util/3rdparty/MurmurHash3.h create mode 100644 src/util/3rdparty/dtoa_milo.h create mode 100644 src/util/PriorityQueue.h create mode 100644 src/util/PriorityQueue.tpp create mode 100644 src/util/geo/CircularSegment.h create mode 100644 src/util/geo/CircularSegment.tpp create mode 100644 src/util/geo/QuadTree.h create mode 100644 src/util/geo/QuadTree.tpp create mode 100644 src/util/graph/BiDijkstra.cpp create mode 100644 src/util/graph/BiDijkstra.h create mode 100644 src/util/graph/BiDijkstra.tpp create mode 100644 src/util/graph/radix_heap.h create mode 100644 src/util/graph/robin/robin_growth_policy.h create mode 100644 src/util/graph/robin/robin_hash.h create mode 100644 src/util/graph/robin/robin_map.h create mode 100644 src/util/graph/robin/robin_set.h create mode 100644 src/util/tests/QuadTreeTest.cpp create mode 100644 src/util/tests/QuadTreeTest.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 008bd55..9239071 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,21 +17,10 @@ enable_testing() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build") - -find_package(OpenMP) -if (OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") -endif() - - # set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake -if(OPENMP_FOUND) - set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") -else() - message(WARNING "Configuring without OpenMP!") - set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") -endif() +set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") +set(CMAKE_CXX_FLAGS_SANITIZE "-Og -g -fsanitize=address -fsanitize=leak -fsanitize=undefined -DLOGLEVEL=3 -DPFAEDLE_DBG=1") +set(CMAKE_CXX_FLAGS_PROFILE "-g -pg -DLOGLEVEL=3 -DPFAEDLE_DBG=1") set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3 -DPFAEDLE_DBG=1") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") @@ -81,14 +70,7 @@ add_subdirectory(src) # tests add_test("utilTest" utilTest) - -# custom eval target - -add_custom_target( - eval - COMMAND make - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval -) +add_test("pfaedleTest" pfaedleTest) # handles install target install( @@ -99,3 +81,8 @@ install( FILES build/pfaedle DESTINATION bin PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries ) + +install( + FILES build/shapevl DESTINATION bin + PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries +) diff --git a/README.md b/README.md index 2694004..6666430 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://tr # pfaedle Precise OpenStreetMap (OSM) map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data). -Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf). ## Requirements @@ -96,48 +95,8 @@ The following flags may be useful for debugging: * `-T ` only calculate shape for a single trip (specified via its GTFS trip id) and output it as GeoJSON to `/path.json` * `--write-graph` write the graph used for routing as GeoJSON to - `/graph.json` - * `--write-cgraph` if `-T` is set, write the combination graph used for - routing as GeoJSON to `/combgraph.json` * `--write-trgraph` write the complete network graph to `/trgraph.json` # Configuration A default configuration file `pfaedle.cfg` can be found in this repo and will be installed with `make install`. Custom configuration files can be specified with the `-c` flag. If no `-c` flag is set, `pfaedle` will parse and merge the following cfg files in the given order (if present): `/etc/pfaedle/pfaedle.cfg`, `$HOME/.config/pfaedle/pfaedle.cfg`, `/pfaedle.cfg`. Values given in later files will overwrite earlier defined values. - -# Evaluation - -You may run an entire evaluation of our testing datasets Vitoria-Gasteiz, Paris, Switzerland and -Stuttgart with - -``` -mkdir build && cd build -cmake .. -make -j -make eval -``` - -*Notes:* - * this will download, and filter, the entire OSM files for Spain and the -Stuttgart region. Make sure you have enough space left on your hard drive. - * in evaluation mode, pfaedle needs significantly more time, because the - calculation of the similarity measurements between shapes are expensive - * if you are only interested in the end results of a single dataset, run - `make .lighteval` in `/eval`. For example, `make paris.lighteval` - generates a shaped version of the paris dataset, without doing extensive - comparisons to the ground truth. - * similarily, if you want to run the extensive evaluation for a single dataset, - run `make .eval` in `/eval`. - - -## Evaluation requirements - - * zlib - -On Debianesque systems, type - -``` -sudo apt-get install zlib1g-dev -``` - -to install the dependencies. diff --git a/eval/Makefile b/eval/Makefile deleted file mode 100644 index 1cae7a8..0000000 --- a/eval/Makefile +++ /dev/null @@ -1,133 +0,0 @@ -EVAL_DF_BINS=10,20,30,40,50,60,70,80,90,100 - -all: eval lighteval - -lighteval: vitoria.lighteval stuttgart.lighteval paris.lighteval switzerland.lighteval - -eval: vitoria.eval stuttgart.eval paris.eval switzerland.eval - -clean: - @rm -f *.eval - @rm -rf gtfs - @rm -rf osm - @rm -rf evalout - -osmconvert: - @echo `date +"[%F %T.%3N]"` "EVAL : Fetching osmconvert..." - @curl http://m.m.i24.cc/osmconvert.c | cc -x c - -lz -O3 -o osmconvert - -%.lighteval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg - @echo `date +"[%F %T.%3N]"` "EVAL : Running light (without stats) evaluation for '"$*"'..." - @mkdir -p gtfs/$*/shaped - @rm -f gtfs/$*/shaped/* - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg -o gtfs/$*/shaped -D -m all 2>&1 | tee $@ - -%.eval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg eval-wo-osm-line-rels.cfg - @echo `date +"[%F %T.%3N]"` "EVAL : Running evaluation for '"$*"'..." - @mkdir -p gtfs/$*/shaped - @rm -f gtfs/$*/shaped/* - @mkdir -p evalout/ - @mkdir -p evalout/$*/ - @mkdir -p evalout/$*/hmm+osm - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --eval-path evalout/$*/hmm+osm -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/hmm+osm/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/greedy - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --method greedy --eval-path evalout/$*/greedy -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/greedy/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/greedy2 - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --method greedy2 --eval-path evalout/$*/greedy2 -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/greedy2/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/hmm - @../build/pfaedle -x $< -i gtfs/$* -c eval-wo-osm-line-rels.cfg --eval-path evalout/$*/hmm -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/hmm/ -name "*.json" -print0 | xargs -0 rm - -osm/spain-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Spain..." - @curl --progress-bar http://download.geofabrik.de/europe/spain-latest.osm.pbf > $@ - -osm/spain-latest.osm: osm/spain-latest.osm.pbf osmconvert - @# pre-filter to vitoria gasteiz - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Vitoria-Gasteiz..." - @osmconvert -b=-2.8661,42.7480,-2.4788,43.0237 $< > $@ - -osm/baden-wuerttemberg-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Baden-Württemberg..." - @curl --progress-bar http://download.geofabrik.de/europe/germany/baden-wuerttemberg-latest.osm.pbf > $@ - -osm/baden-wuerttemberg-latest.osm: osm/baden-wuerttemberg-latest.osm.pbf osmconvert - @echo `date +"[%F %T.%3N]"` "EVAL : Extracting OSM data..." - @osmconvert $< > $@ - -osm/france-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for France..." - @curl --progress-bar http://download.geofabrik.de/europe/france-latest.osm.pbf > $@ - -osm/paris-latest.osm: osm/france-latest.osm.pbf osmconvert - @# pre-filter to greater ile de france - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Île-de-France..." - @osmconvert -b=0.374,47.651,4.241,50.261 $< > $@ - -osm/europe-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Europe..." - @curl --progress-bar http://download.geofabrik.de/europe-latest.osm.pbf > $@ - -osm/switzerland-latest.osm: osm/europe-latest.osm.pbf osmconvert - @# pre-filter to greater switzerland - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Switzerland..." - @osmconvert -b=3.757,44.245,15.579,52.670 $< > $@ - -gtfs/vitoria/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Vitoria-Gasteiz..." - @mkdir -p gtfs - @mkdir -p gtfs/vitoria - @curl --progress-bar https://transitfeeds.com/p/tuvisa-euskotran/239/latest/download > gtfs/vitoria/gtfs.zip - @cd gtfs/vitoria && unzip -qq -o gtfs.zip - @rm gtfs/vitoria/gtfs.zip - -gtfs/stuttgart/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Stuttgart..." - @mkdir -p gtfs - @mkdir -p gtfs/stuttgart - @echo "******************************************************************" - @echo "* A password is required to access the VVS dataset. Send a mail *" - @echo "* to brosi@cs.informatik.uni-freiburg.de to receive the password. " - @echo "******************************************************************" - @curl --progress-bar http://www.vvs.de/download/opendata/VVS_GTFS.zip -su vvsopendata01 > gtfs/stuttgart/gtfs.zip - @cd gtfs/stuttgart && unzip -qq -o gtfs.zip - @rm gtfs/stuttgart/gtfs.zip - -gtfs/paris/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Paris..." - @mkdir -p gtfs - @mkdir -p gtfs/paris - @curl --progress-bar https://transitfeeds.com/p/stif/822/latest/download > gtfs/paris/gtfs.zip - @cd gtfs/paris && unzip -qq -o gtfs.zip - @rm gtfs/paris/gtfs.zip - -gtfs/switzerland/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Switzerland..." - @mkdir -p gtfs - @mkdir -p gtfs/switzerland - @curl --progress-bar http://gtfs.geops.ch/dl/gtfs_complete.zip > gtfs/switzerland/gtfs.zip - @cd gtfs/switzerland && unzip -qq -o gtfs.zip - @rm gtfs/switzerland/gtfs.zip - - -osm/vitoria.osm: osm/spain-latest.osm gtfs/vitoria/stops.txt gtfs/vitoria/trips.txt gtfs/vitoria/routes.txt gtfs/vitoria/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/vitoria/ -c eval.cfg -m all -X $@ - -osm/stuttgart.osm: osm/baden-wuerttemberg-latest.osm gtfs/stuttgart/stops.txt gtfs/stuttgart/trips.txt gtfs/stuttgart/routes.txt gtfs/stuttgart/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/stuttgart/ -c eval.cfg -m all -X $@ - -osm/paris.osm: osm/paris-latest.osm gtfs/paris/stops.txt gtfs/paris/trips.txt gtfs/paris/routes.txt gtfs/paris/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/paris/ -c eval.cfg -m all -X $@ - -osm/switzerland.osm: osm/switzerland-latest.osm gtfs/switzerland/stops.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/switzerland/ -c eval.cfg -m all -X $@ diff --git a/eval/eval-wo-osm-line-rels.cfg b/eval/eval-wo-osm-line-rels.cfg deleted file mode 100644 index e37f2b8..0000000 --- a/eval/eval-wo-osm-line-rels.cfg +++ /dev/null @@ -1,987 +0,0 @@ -# Copyright 2018, University of Freiburg -# Chair of Algorithms and Datastructures -# Authors: Patrick Brosi - -[rail] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - railway=rail - railway=light_rail - railway=narrow_gauge - route=rail - route=train - public_transport=stop_area|rel_flat - -osm_filter_lvl1: - usage=branch - -osm_filter_lvl2: - -osm_filter_lvl3: - service=crossover - service=siding - # we cannot completely drop service=yard, because it is often used - # incorrectly for crossovers - service=yard - -osm_filter_lvl4: - -osm_filter_lvl5: - usage=industrial - usage=military - usage=test - service=spur - railway:traffic_mode=freight - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - railway=abandoned - railway=construction - railway=disused - railway=miniature - railway=signal - railway=razed - railway=proposed - metro=yes - area=yes - # access=no - type=multipolygon - railway=platform - public_transport=platform - building=yes - building=train_station - amenity=shelter - amenity=bus_station - building=roof - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - railway:preferred_direction=forward - -osm_filter_oneway_reverse: - railway:preferred_direction=backward - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=no - oneway=-1 - railway:preferred_direction=both - railway:bidirectional=regular - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - railway=stop - railway=halt - railway=station - #railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -# max edge level to which station will be snapped -osm_max_snap_level: 2 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - [public_transport=stop_area]name - uic_name - -# the track number tag in edges, first match is taken -osm_edge_track_number_tags: - railway:track_ref - local_ref - ref - -# the track number tag in stop nodes, first match is taken, -# overwrites osm_edge_track_number_tags -osm_track_number_tags: - local_ref - ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 3000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 100 - -routing_platform_unmatched_punish: 2000 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 100 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 100 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 - -# special line normalization for trains -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - ç -> c; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS with whitespace (ICE101 -> ICE 101) - ^([a-zA-Z]+)([0-9]+)$ -> \1 \2; - - # if a character line number is present, delete the numeric part - ^([a-zA-Z]+) [0-9]+$ -> \1; - -[bus] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - # highways - highway=motorway - highway=trunk - highway=primary - highway=secondary - highway=tertiary - highway=residential - highway=living_street - highway=unclassified - - # highway links - highway=motorway_link - highway=trunk_link - highway=primary_link - highway=secondary_link - highway=tertiary_link - highway=residential_link - - way=primary - way=seconday - way=bus_guideway - highway=bus_guideway - busway=* - psv=yes - psv=designated - - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - - route=bus - route=trolleybus - bus=yes - bus=designated - minibus=designated - minibus=yes - - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station|no_match_ways|no_match_rels - - # relations for the restriction system - type=restriction - type=restriction:bus - type=restriction:motorcar - -osm_filter_lvl1: - highway=secondary - highway=secondary_link - bus=yes - bus=designated - minibus=yes - minibus=designated - psv=designated - psv=yes - access=psv - access=bus - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - psv=designated - -osm_filter_lvl2: - highway=tertiary - highway=tertiary_link - -osm_filter_lvl3: - highway=unclassified - highway=residential - highway=road - -osm_filter_lvl4: - highway=living_street - highway=pedestrian - highway=service - psv=no - -osm_filter_lvl5: - bus=no - service=siding - access=permissive - access=private - access=no - service=parking_aisle - highway=footway - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - train=yes|no_match_ways - # access=no - public_transport=stop_area|no_match_nds|no_match_rels - type=multipolygon - railway=platform - railway=station - # service=parking_aisle - highway=proposed - highway=footway - highway=construction - building=yes - building=train_station - leisure=garden - leisure=park - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - -# Configuration of the OSM road restriction system -# We only support restriction with a single via node -# atm - -osm_node_negative_restriction: - restriction=no_right_turn - restriction=no_left_turn - restriction=no_u_turn - restriction=no_straight_on - restriction:bus=no_right_turn - restriction:bus=no_left_turn - restriction:bus=no_u_turn - restriction:bus=no_straight_on - -osm_node_positive_restriction: - restriction=only_left_turn - restriction=only_straight_on - restriction=only_right_turn - restriction:bus=only_left_turn - restriction:bus=only_straight_on - restriction:bus=only_right_turn - -osm_filter_no_restriction: - except=psv|mult_val_match - except=bus|mult_val_match - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - junction=roundabout # oneway=yes is implied - highway=motorway # oneway=yes is implied - oneway=yes - oneway=1 - oneway=true - oneway:bus=yes - oneway:bus=1 - oneway:bus=true - oneway:psv=yes - oneway:psv=1 - oneway:psv=true - -osm_filter_oneway_reverse: - oneway=-1 - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=0 - oneway=alternating - oneway=reversible - oneway=no - oneway:bus=no - oneway:bus=0 - oneway:bus=false - oneway:psv=no - oneway:psv=0 - oneway:psv=false - busway=opposite_lane - busway=opposite - busway:left=opposite_lane - busway:right=opposite_lane - psv=opposite_lane - psv=opposite - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10 , 50, 100 - -osm_max_snap_level: 5 - -osm_max_osm_station_distance: 7.5 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 500 - -routing_station_distance_punish_fac: 2.5 - -routing_non_osm_station_punish: 500 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 20 - -# Max angle in a route from a station to an already reachable neighbor -routing_snap_full_turn_angle: 110 - -osm_max_node_block_distance: 10 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -[tram, subway, funicular] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=tram - railway=subway - railway=light_rail - railway=tram - railway=funicular - railway=station - railway=halt - railway=tram_stop - route=subway - route=light_rail - subway=yes - tram=yes - -osm_filter_lv2: - service=siding - -osm_filter_lvl5: - service=crossover - service=yard - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - public_transport=stop_area - type=multipolygon - railway=platform - public_transport=platform - service=alley - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=subway - station=tram - railway=stop - railway=halt - railway=station - railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 2000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 235 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 80 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 80 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[ferry] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=ferry - waterway=river - motorboat=yes - ferry=yes - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=ferry - railway=stop - railway=halt - railway=station - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 100 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 50 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 45 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 0 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[tram, bus, subway, rail, gondola, funicular, ferry] -# Regular expressions and station comparision is -# always case insensitive! -station_normalize_chain: - , -> ' '; - - -> ' '; - — -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - \( -> ' '; - \) -> ' '; - \[ -> ' '; - \] -> ' '; - / -> ' '; - '\\' -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - ô -> o; - ç -> c; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - str\. -> strasse; - av\. -> avenue; - - # always separate 'street', 'strasse' - '([a-zA-Z])strasse($| )' -> '\1 strasse\2'; - '([a-zA-Z])street($| )' -> '\1 street\2'; - - # always use "street" - '(^| )strasse($| )' -> '\1street\2'; - - # always use "avenue" - '(^| )avenida($| )' -> '\1avenue\2'; - '(^| )avenu($| )' -> '\1avenue\2'; - - # normalize every possible abbr. of german "Bahnhof", "Hauptbahnhof", "Busbahnhof" - '(^| )hauptbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf($| )' -> '\1hauptbahnhof\2'; - '(^| )zentraler busbahnhof($| )$' -> \1busbahnhof\2; - '(^| )zentraler omnibusbahnhof($| )$' -> \1busbahnhof\2; - '(^| )omnibusbahnhof($| )' -> '\1busbahnhof\2'; - '(^| )omnibusbhf($| )' -> '\1busbahnhof\2'; - '(^| )busbf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbf($| )' -> '\1busbahnhof\2'; - '(^| )bus bf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bf($| )' -> '\1busbahnhof\2'; - '(^| )busbhf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbhf($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf($| )' -> '\1busbahnhof\2'; - '(^| )zob($| )' -> '\1busbahnhof\2'; - '(^| )hbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hb\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hb($| )' -> '\1hauptbahnhof\2'; - '(^| )bf\.($| )' -> '\1bahnhof\2'; - '(^| )bf($| )' -> '\1bahnhof\2'; - '(^| )bhf\.($| )' -> '\1bahnhof\2'; - '(^| )bhf($| )' -> '\1bahnhof\2'; - '(^| )bhfeingang($| )' -> '\1bahnhofeingang\2'; - '(^| )gare de($| )' -> '\1gare\2'; - - - # if a stations starts with single station identifier - # always put it at the end (for example, "hauptbahnhof freiburg" becomes "freiburg hauptbahnhof") - '^hauptbahnhof (.+)$' -> \1 hauptbahnhof; - '^bahnhof (.+)$' -> \1 bahnhof; - '^busbahnhof (.+)$' -> \1 busbahnhof; - '^gare (.+)$' -> \1 gare; - '^station (.+)$' -> \1 station; - - '(^| )busbahnhof($| )' -> '\1bbahnhof\2'; - - # normalize line types in station names - '(^| )u bahn\.($| )' -> '\1ubahn\2'; - '(^| )metro\.($| )' -> '\1ubahn\2'; - '(^| )subway\.($| )' -> '\1ubahn\2'; - '(^| )underground\.($| )' -> '\1ubahn\2'; - '(^| )ubahn($| )' -> '\1u\2'; - '(^| )s bahn\.($| )' -> '\1sbahn\2'; - '(^| )sbahn($| )' -> '\1s\2'; - '(^| )tramway($| )' -> '\1tram\2'; - '(^| )stadtbahn($| )' -> '\1tram\2'; - '(^| )strassenbahn($| )' -> '\1tram\2'; - '(^| )streetcar($| )' -> '\1tram\2'; - '(^| )tram($| )' -> '\1t\2'; - - # delete track information from name - '(^| )kante [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )gleis [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )track [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )voie [a-zA-Z0-9]{1,2}($| )' -> ' '; - - # abbrv - '(^| )und($| )' -> '\1+\2'; - '(^| )and($| )' -> '\1+\2'; - '(^| )et($| )' -> '\1+\2'; - - # noise - '\sde\s' -> ' '; - '\sda\s' -> ' '; - '\sdi\s' -> ' '; - '\sdel\s' -> ' '; - '\sdal\s' -> ' '; - - # abbrv in most western languages - '(^| )saint ' -> '\1st. '; - '(^| )sankt ' -> '\1st. '; - '(^| )sanct ' -> '\1st. '; - - \. -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - ç -> c; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS without whitespace (T 2 -> T2) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - -track_normalize_chain: - '(^| )gleis($| )' -> ''; - '(^| )gl\.($| )' -> ''; - '(^| )platform($| )' -> ''; - '(^| )track($| )' -> ''; - '(^| )rail($| )' -> ''; - # line/number combs ALWAYS without whitespace (1 A -> 1A) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - ^([0-9]+) ([a-zA-Z]+)$ -> \1\2; - - # delete track numbers greater than 999 - ^[0-9]{4,}$ -> ''; diff --git a/eval/eval.cfg b/eval/eval.cfg deleted file mode 100644 index ab4189c..0000000 --- a/eval/eval.cfg +++ /dev/null @@ -1,1002 +0,0 @@ -# Copyright 2018, University of Freiburg -# Chair of Algorithms and Datastructures -# Authors: Patrick Brosi - -[rail] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - railway=rail - railway=light_rail - railway=narrow_gauge - route=rail - route=train - public_transport=stop_area|rel_flat - -osm_filter_lvl1: - usage=branch - -osm_filter_lvl2: - -osm_filter_lvl3: - service=crossover - service=siding - # we cannot completely drop service=yard, because it is often used - # incorrectly for crossovers - service=yard - -osm_filter_lvl4: - -osm_filter_lvl5: - usage=industrial - usage=military - usage=test - service=spur - railway:traffic_mode=freight - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - railway=abandoned - railway=construction - railway=disused - railway=miniature - railway=signal - railway=razed - railway=proposed - metro=yes - area=yes - # access=no - type=multipolygon - railway=platform - public_transport=platform - building=yes - building=train_station - amenity=shelter - amenity=bus_station - building=roof - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - railway:preferred_direction=forward - -osm_filter_oneway_reverse: - railway:preferred_direction=backward - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=no - oneway=-1 - railway:preferred_direction=both - railway:bidirectional=regular - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - railway=stop - railway=halt - railway=station - #railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -# max edge level to which station will be snapped -osm_max_snap_level: 2 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - [public_transport=stop_area]name - uic_name - -# the track number tag in edges, first match is taken -osm_edge_track_number_tags: - railway:track_ref - local_ref - ref - -# the track number tag in stop nodes, first match is taken, -# overwrites osm_edge_track_number_tags -osm_track_number_tags: - local_ref - ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 3000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 100 - -routing_platform_unmatched_punish: 2000 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 100 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 100 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 - -# special line normalization for trains -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - ç -> c; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS with whitespace (ICE101 -> ICE 101) - ^([a-zA-Z]+)([0-9]+)$ -> \1 \2; - - # if a character line number is present, delete the numeric part - ^([a-zA-Z]+) [0-9]+$ -> \1; - -[bus] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - # highways - highway=motorway - highway=trunk - highway=primary - highway=secondary - highway=tertiary - highway=residential - highway=living_street - highway=unclassified - - # highway links - highway=motorway_link - highway=trunk_link - highway=primary_link - highway=secondary_link - highway=tertiary_link - highway=residential_link - - way=primary - way=seconday - way=bus_guideway - highway=bus_guideway - busway=* - psv=yes - psv=designated - - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - - route=bus - route=trolleybus - bus=yes - bus=designated - minibus=designated - minibus=yes - - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station|no_match_ways|no_match_rels - - # relations for the restriction system - type=restriction - type=restriction:bus - type=restriction:motorcar - -osm_filter_lvl1: - highway=secondary - highway=secondary_link - bus=yes - bus=designated - minibus=yes - minibus=designated - psv=designated - psv=yes - access=psv - access=bus - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - psv=designated - -osm_filter_lvl2: - highway=tertiary - highway=tertiary_link - -osm_filter_lvl3: - highway=unclassified - highway=residential - highway=road - -osm_filter_lvl4: - highway=living_street - highway=pedestrian - highway=service - psv=no - -osm_filter_lvl5: - bus=no - service=siding - access=permissive - access=private - access=no - service=parking_aisle - highway=footway - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - train=yes|no_match_ways - # access=no - public_transport=stop_area|no_match_nds|no_match_rels - type=multipolygon - railway=platform - railway=station - # service=parking_aisle - highway=proposed - highway=footway - highway=construction - building=yes - building=train_station - leisure=garden - leisure=park - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - -# Configuration of the OSM road restriction system -# We only support restriction with a single via node -# atm - -osm_node_negative_restriction: - restriction=no_right_turn - restriction=no_left_turn - restriction=no_u_turn - restriction=no_straight_on - restriction:bus=no_right_turn - restriction:bus=no_left_turn - restriction:bus=no_u_turn - restriction:bus=no_straight_on - -osm_node_positive_restriction: - restriction=only_left_turn - restriction=only_straight_on - restriction=only_right_turn - restriction:bus=only_left_turn - restriction:bus=only_straight_on - restriction:bus=only_right_turn - -osm_filter_no_restriction: - except=psv|mult_val_match - except=bus|mult_val_match - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - junction=roundabout # oneway=yes is implied - highway=motorway # oneway=yes is implied - oneway=yes - oneway=1 - oneway=true - oneway:bus=yes - oneway:bus=1 - oneway:bus=true - oneway:psv=yes - oneway:psv=1 - oneway:psv=true - -osm_filter_oneway_reverse: - oneway=-1 - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=0 - oneway=alternating - oneway=reversible - oneway=no - oneway:bus=no - oneway:bus=0 - oneway:bus=false - oneway:psv=no - oneway:psv=0 - oneway:psv=false - busway=opposite_lane - busway=opposite - busway:left=opposite_lane - busway:right=opposite_lane - psv=opposite_lane - psv=opposite - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10 , 50, 100 - -osm_max_snap_level: 5 - -osm_max_osm_station_distance: 7.5 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 500 - -routing_station_distance_punish_fac: 2.5 - -routing_non_osm_station_punish: 500 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 20 - -# Max angle in a route from a station to an already reachable neighbor -routing_snap_full_turn_angle: 110 - -osm_max_node_block_distance: 10 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -[tram, subway, funicular] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=tram - railway=subway - railway=light_rail - railway=tram - railway=funicular - railway=station - railway=halt - railway=tram_stop - route=subway - route=light_rail - subway=yes - tram=yes - -osm_filter_lv2: - service=siding - -osm_filter_lvl5: - service=crossover - service=yard - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - public_transport=stop_area - type=multipolygon - railway=platform - public_transport=platform - service=alley - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=subway - station=tram - railway=stop - railway=halt - railway=station - railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 2000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 235 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 80 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 80 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[ferry] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=ferry - waterway=river - motorboat=yes - ferry=yes - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=ferry - railway=stop - railway=halt - railway=station - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 100 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 50 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 45 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 0 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[tram, bus, subway, rail, gondola, funicular, ferry] -# Regular expressions and station comparision is -# always case insensitive! -station_normalize_chain: - , -> ' '; - - -> ' '; - — -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - \( -> ' '; - \) -> ' '; - \[ -> ' '; - \] -> ' '; - / -> ' '; - '\\' -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - ô -> o; - ç -> c; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - str\. -> strasse; - av\. -> avenue; - - # always separate 'street', 'strasse' - '([a-zA-Z])strasse($| )' -> '\1 strasse\2'; - '([a-zA-Z])street($| )' -> '\1 street\2'; - - # always use "street" - '(^| )strasse($| )' -> '\1street\2'; - - # always use "avenue" - '(^| )avenida($| )' -> '\1avenue\2'; - '(^| )avenu($| )' -> '\1avenue\2'; - - # normalize every possible abbr. of german "Bahnhof", "Hauptbahnhof", "Busbahnhof" - '(^| )hauptbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf($| )' -> '\1hauptbahnhof\2'; - '(^| )zentraler busbahnhof($| )$' -> \1busbahnhof\2; - '(^| )zentraler omnibusbahnhof($| )$' -> \1busbahnhof\2; - '(^| )omnibusbahnhof($| )' -> '\1busbahnhof\2'; - '(^| )omnibusbhf($| )' -> '\1busbahnhof\2'; - '(^| )busbf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbf($| )' -> '\1busbahnhof\2'; - '(^| )bus bf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bf($| )' -> '\1busbahnhof\2'; - '(^| )busbhf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbhf($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf($| )' -> '\1busbahnhof\2'; - '(^| )zob($| )' -> '\1busbahnhof\2'; - '(^| )hbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hb\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hb($| )' -> '\1hauptbahnhof\2'; - '(^| )bf\.($| )' -> '\1bahnhof\2'; - '(^| )bf($| )' -> '\1bahnhof\2'; - '(^| )bhf\.($| )' -> '\1bahnhof\2'; - '(^| )bhf($| )' -> '\1bahnhof\2'; - '(^| )bhfeingang($| )' -> '\1bahnhofeingang\2'; - '(^| )gare de($| )' -> '\1gare\2'; - - - # if a stations starts with single station identifier - # always put it at the end (for example, "hauptbahnhof freiburg" becomes "freiburg hauptbahnhof") - '^hauptbahnhof (.+)$' -> \1 hauptbahnhof; - '^bahnhof (.+)$' -> \1 bahnhof; - '^busbahnhof (.+)$' -> \1 busbahnhof; - '^gare (.+)$' -> \1 gare; - '^station (.+)$' -> \1 station; - - '(^| )busbahnhof($| )' -> '\1bbahnhof\2'; - - # normalize line types in station names - '(^| )u bahn\.($| )' -> '\1ubahn\2'; - '(^| )metro\.($| )' -> '\1ubahn\2'; - '(^| )subway\.($| )' -> '\1ubahn\2'; - '(^| )underground\.($| )' -> '\1ubahn\2'; - '(^| )ubahn($| )' -> '\1u\2'; - '(^| )s bahn\.($| )' -> '\1sbahn\2'; - '(^| )sbahn($| )' -> '\1s\2'; - '(^| )tramway($| )' -> '\1tram\2'; - '(^| )stadtbahn($| )' -> '\1tram\2'; - '(^| )strassenbahn($| )' -> '\1tram\2'; - '(^| )streetcar($| )' -> '\1tram\2'; - '(^| )tram($| )' -> '\1t\2'; - - # delete track information from name - '(^| )kante [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )gleis [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )track [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )voie [a-zA-Z0-9]{1,2}($| )' -> ' '; - - # abbrv - '(^| )und($| )' -> '\1+\2'; - '(^| )and($| )' -> '\1+\2'; - '(^| )et($| )' -> '\1+\2'; - - # noise - '\sde\s' -> ' '; - '\sda\s' -> ' '; - '\sdi\s' -> ' '; - '\sdel\s' -> ' '; - '\sdal\s' -> ' '; - - # abbrv in most western languages - '(^| )saint ' -> '\1st. '; - '(^| )sankt ' -> '\1st. '; - '(^| )sanct ' -> '\1st. '; - - \. -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - ç -> c; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS without whitespace (T 2 -> T2) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - -track_normalize_chain: - '(^| )gleis($| )' -> ''; - '(^| )gl\.($| )' -> ''; - '(^| )platform($| )' -> ''; - '(^| )track($| )' -> ''; - '(^| )rail($| )' -> ''; - # line/number combs ALWAYS without whitespace (1 A -> 1A) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - ^([0-9]+) ([a-zA-Z]+)$ -> \1\2; - - # delete track numbers greater than 999 - ^[0-9]{4,}$ -> ''; diff --git a/geo/pfaedle.qgs b/geo/pfaedle.qgs index 2ec03bf..aa197d3 100644 --- a/geo/pfaedle.qgs +++ b/geo/pfaedle.qgs @@ -1,176 +1,140 @@ - - + - - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator - merc - WGS84 - false - - - + - + - + - + - + - + - + - + - - + + - + - + - + - + - - - - - - - + - + - - OGRGeoJSON_Point20180203134333739 - OGRGeoJSON_LineString20180203134333975 - OGRGeoJSON_Point20180206114956218 - OGRGeoJSON_LineString20180206114956229 - path20180217155708341 - trgraph_trgraph_LineString20180508200527144 - trgraph_trgraph_Point20180508200527256 - OSM_Transportation20181215024818603 - OpenStreetMap_de20181215024846026 - - - - - - - - - - - - - + meters - -374853.74009754881262779 - 4605645.85081499628722668 - 2640376.65992686524987221 - 7553306.65061968378722668 + 866081.24618305882904679 + 6076552.62015097495168447 + 870662.44395622855518013 + 6080842.96235341485589743 0 + 1 - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator + +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs + 1353 + 3395 + EPSG:3395 + WGS 84 / World Mercator merc WGS84 false 0 + + + + + + + - + + + path20180217155708341 + OGRGeoJSON_Point20180203134333739 + OGRGeoJSON_LineString20180203134333975 + OGRGeoJSON_Point20180206114956218 + OGRGeoJSON_LineString20180206114956229 + OSM_Transportation20181215024818603 + OpenStreetMap_de20181215024846026 + + - + - - - - - - - - + - - + 6.70734330570000026 47.04982400000000098 @@ -195,1730 +159,1127 @@ true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -1941,55 +1302,35 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.05522500000000008 @@ -2014,480 +1355,129 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - + + + + + ogr - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -2510,53 +1500,35 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.04982400000000098 @@ -2581,615 +1553,326 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -3212,59 +1895,39 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.05522500000000008 @@ -3289,432 +1952,208 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -3737,53 +2176,34 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -3803,70 +2223,33 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo-Mercator + WGS 84 / Pseudo Mercator merc WGS84 false - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - + + + wms - + - - 1 - 1 - 1 - - - - - + - - None - WholeRaster - Estimated - 0.02 - 0.98 - 2 - - + 0 - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -3886,70 +2269,33 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo-Mercator + WGS 84 / Pseudo Mercator merc WGS84 false - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - + + + wms - + - - 1 - 1 - 1 - - - - - + - - None - WholeRaster - Estimated - 0.02 - 0.98 - 2 - - + 0 - + 6.70738836720000009 47.05522500000000008 @@ -3974,193 +2320,147 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + + - + 0 0 - 0.7 - - - - + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + - - - - - - + + - + - - - - - - . + . 0 . @@ -4183,1965 +2483,96 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - ver - - - trgraph_trgraph_LineString20180508200527144 - ./trgraph.json|layerid=0|geometrytype=LineString - - - - trgraph trgraph LineString - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - - ogr - - - - - - - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - - 0 - . - - 0 - generatedlayout - - - - - id - - - trgraph_trgraph_Point20180508200527256 - ./trgraph.json|layerid=0|geometrytype=Point - - - - trgraph trgraph Point - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - - ogr - - - - - - - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - - 0 - . - - 0 - generatedlayout - - - - id - - - - - - - - - - - - - 255 - - - - - true - - - - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - EPSG:3857 - 3857 - 1 - - - meters - m2 - - - - - - false - - - - - - - - - - 30 - false - true - 0 - false - 0 - 16 - 50 - false - - false - - - 2 - D - true - - - - - - conditions unknown - - false - false - - 255 - 255 - 240 - 0 - 240 - 255 - 240 - - 90 + - + false - - - - 2 - 2 - 2 - 2 - 2 - 2 - 2 - - off - - OGRGeoJSON_LineString20180203134333975 - OGRGeoJSON_LineString20180206114956229 - OGRGeoJSON_Point20180203134333739 - OGRGeoJSON_Point20180206114956218 - path20180217155708341 - trgraph_trgraph_LineString20180508200527144 - trgraph_trgraph_Point20180508200527256 - - - - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - 0 - 2 - - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex_and_segment - to_vertex_and_segment - - - enabled - enabled - enabled - enabled - enabled - disabled - disabled - - current_layer - + + + + + + false + + + + + + 2 + true + MU + + + false + + + + false WGS84 - None + 8 + false + + + + + + + 0 + 255 + 255 + 255 + 255 + 255 + 255 + + + + - false - - 8 + None + false + + +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs + EPSG:3395 + 1353 + 1 + + + + + + true + 255 + + + conditions unknown + 90 + + meters + m2 + + - - - - - - - - - - - - - - diff --git a/pfaedle.cfg b/pfaedle.cfg index 8840534..1ec3a62 100644 --- a/pfaedle.cfg +++ b/pfaedle.cfg @@ -3,6 +3,10 @@ # Authors: Patrick Brosi [tram, bus, coach, subway, rail, gondola, funicular, ferry] + +routing_transition_penalty_fac: 0.0083 +routing_station_move_penalty_fac: 0.00087 + # Regular expressions and station comparision is # always case insensitive! station_normalize_chain: @@ -334,22 +338,9 @@ osm_line_relation_tags: from_name=from to_name=to -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 200 # max edge level to which station will be snapped osm_max_snap_level: 2 @@ -372,24 +363,31 @@ osm_track_number_tags: local_ref ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 120 # default level +osm_lvl1_avg_speed: 90 +osm_lvl2_avg_speed: 65 +osm_lvl3_avg_speed: 50 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 3000 +routing_full_turn_penalty: 180 # 3 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 100 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 -routing_platform_unmatched_punish: 2000 +# If the platform does not match, add this penalty +routing_platform_unmatched_penalty: 0.1 + +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 100 @@ -397,24 +395,25 @@ routing_full_turn_angle: 100 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 100 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 5 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# Additional one-time time penalty for entering a one-way segment +# in seconds +osm_one_way_entry_cost: 300 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 # special line normalization for trains line_normalize_chain: @@ -558,6 +557,12 @@ osm_filter_keep: type=restriction:motorcar osm_filter_lvl1: + highway=trunk + highway=trunk_link + highway=primary + highway=primary_link + +osm_filter_lvl2: highway=secondary highway=secondary_link bus=yes @@ -574,22 +579,22 @@ osm_filter_lvl1: trolley_bus=yes psv=designated -osm_filter_lvl2: +osm_filter_lvl3: highway=tertiary highway=tertiary_link -osm_filter_lvl3: +osm_filter_lvl4: highway=unclassified highway=residential highway=road -osm_filter_lvl4: +osm_filter_lvl5: highway=living_street highway=pedestrian highway=service psv=no -osm_filter_lvl5: +osm_filter_lvl6: bus=no service=siding access=permissive @@ -597,6 +602,7 @@ osm_filter_lvl5: access=no service=parking_aisle highway=footway + highway=track # OSM entities to drop, as k=v. Applies to nodes, edges and # relations. @@ -714,6 +720,12 @@ osm_filter_station: highway=bus_stop amenity=bus_station +osm_filter_turning_cycle: + highway=turning_cycle + highway=turning_loop + junction=roundabout + highway=mini_roundabout + # Relation fields that should be used for catching the lines that # occur on an edge. Only relations that have been kept during the # filtering above will be checked. The 'linename' will be normalized @@ -727,26 +739,20 @@ osm_line_relation_tags: line_name=ref,name # careful, no space after/before comma allowed! from_name=from to_name=to + line_color=colour,color +# max distance in meters between a OSM station candidate +# and the input GTFS station +osm_max_station_cand_distance: 200 -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_fallback_distance: 300 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 5 +# max distance between a snapped OSM station and is snap +# point to still be considered a "OSM station" osm_max_osm_station_distance: 8.0 # sorted by priority, first found attr will be taken @@ -757,22 +763,48 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 5 + +# Additional one-time time penalty for entering a one-way segment +# in seconds +osm_one_way_entry_cost: 300 + +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 + +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 + +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.1 + +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 500 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 2.5 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 500 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 20 @@ -782,26 +814,6 @@ routing_snap_full_turn_angle: 110 osm_max_node_block_distance: 10 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 [coach] @@ -850,14 +862,14 @@ osm_filter_lvl7: service=parking_aisle highway=footway -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.15 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 2.5 -routing_lvl6_fac: 3 -routing_lvl7_fac: 4 +osm_lvl0_avg_speed: 120 # default level +osm_lvl1_avg_speed: 90 +osm_lvl2_avg_speed: 65 +osm_lvl3_avg_speed: 50 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 osm_max_snap_level: 5 @@ -958,20 +970,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -984,22 +985,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 180 # 3 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1007,24 +1011,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [gondola] @@ -1104,20 +1105,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -1130,22 +1120,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1153,24 +1146,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [funicular] @@ -1281,20 +1271,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -1307,22 +1286,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1330,24 +1312,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [ferry] @@ -1366,7 +1345,10 @@ osm_filter_keep: # Only nodes that have been kept during the filtering above will be # checked. osm_filter_station: + ferry=yes public_transport=stop_position + amenity=ferry_terminal + mooring=ferry station=ferry railway=stop railway=halt @@ -1387,20 +1369,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 500 osm_max_snap_level: 4 @@ -1413,22 +1384,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 70 # default level +osm_lvl1_avg_speed: 60 +osm_lvl2_avg_speed: 50 +osm_lvl3_avg_speed: 35 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 25 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 100 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 50 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 45 @@ -1436,22 +1410,18 @@ routing_full_turn_angle: 45 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 0 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7d54350..fbf3449 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(util) add_subdirectory(pfaedle) add_subdirectory(cppgtfs) add_subdirectory(configparser) +add_subdirectory(shapevl) diff --git a/src/configparser b/src/configparser index b2d0c99..ca166b3 160000 --- a/src/configparser +++ b/src/configparser @@ -1 +1 @@ -Subproject commit b2d0c99b9c84f62f5f7b259524e0f4b1c9d38318 +Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b diff --git a/src/cppgtfs b/src/cppgtfs index be682b2..6328d02 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit be682b2cc13125147bad9ebe544f0bad25d0bd22 +Subproject commit 6328d026f6fa870c87b18ff93ab6f4b65a869686 diff --git a/src/pfaedle/CMakeLists.txt b/src/pfaedle/CMakeLists.txt index 22b083a..8adb8d6 100644 --- a/src/pfaedle/CMakeLists.txt +++ b/src/pfaedle/CMakeLists.txt @@ -18,3 +18,5 @@ add_library(pfaedle_dep ${pfaedle_SRC}) include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs -lpthread) + +add_subdirectory(tests) diff --git a/src/pfaedle/Def.h b/src/pfaedle/Def.h index 7b8e083..9308161 100644 --- a/src/pfaedle/Def.h +++ b/src/pfaedle/Def.h @@ -17,17 +17,17 @@ #define __str_c(s) s ## 1 #define __str_d(s) __str_c(s) -#if !defined(PFAEDLE_PRECISION) || (__str_d(PFAEDLE_PRECISION) == 1) -#undef PFAEDLE_PRECISION -#define PFAEDLE_PRECISION double +#if !defined(PFDL_PREC) || (__str_d(PFDL_PREC) == 1) +#undef PFDL_PREC +#define PFDL_PREC double #endif -#define PFAEDLE_PRECISION_STR __str_a(PFAEDLE_PRECISION) +#define PFDL_PREC_STR __str_a(PFDL_PREC) -#define POINT util::geo::Point -#define LINE util::geo::Line -#define BOX util::geo::Box -#define POLYLINE util::geo::PolyLine +#define POINT util::geo::Point +#define LINE util::geo::Line +#define BOX util::geo::Box +#define POLYLINE util::geo::PolyLine #define BOX_PADDING 2500 diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 55bea77..4082f16 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -18,19 +18,19 @@ #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" +#include "pfaedle/router/Stats.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/Graph.h" -#include "pfaedle/trgraph/StatGroup.h" +#include "util/Misc.h" #include "util/geo/output/GeoGraphJsonOutput.h" #include "util/geo/output/GeoJsonOutput.h" #include "util/json/Writer.h" #include "util/log/Log.h" -#include "util/Misc.h" #ifndef CFG_HOME_SUFFIX #define CFG_HOME_SUFFIX "/.config" @@ -42,16 +42,25 @@ #define CFG_FILE_NAME "pfaedle.cfg" #endif -using pfaedle::router::MOTs; +using configparser::ParseFileExc; +using pfaedle::config::Config; +using pfaedle::config::ConfigReader; +using pfaedle::config::MotConfig; +using pfaedle::config::MotConfigReader; using pfaedle::osm::BBoxIdx; using pfaedle::osm::OsmBuilder; -using pfaedle::config::MotConfig; -using pfaedle::config::Config; +using pfaedle::router::DistDiffTransWeight; +using pfaedle::router::DistDiffTransWeightNoHeur; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::ExpoTransWeightNoHeur; +using pfaedle::router::MOTs; +using pfaedle::router::NormDistrTransWeight; +using pfaedle::router::NormDistrTransWeightNoHeur; +using pfaedle::router::Router; +using pfaedle::router::RouterImpl; using pfaedle::router::ShapeBuilder; -using configparser::ParseFileExc; -using pfaedle::config::MotConfigReader; -using pfaedle::config::ConfigReader; -using pfaedle::eval::Collector; +using pfaedle::router::Stats; +using pfaedle::statsimiclassifier::JaccardClassifier; enum class RetCode { SUCCESS = 0, @@ -77,6 +86,11 @@ int main(int argc, char** argv) { // initialize randomness srand(time(NULL) + rand()); // NOLINT + // use utf8 locale + std::setlocale(LC_ALL, "en_US.utf8"); + + T_START(total); + Config cfg; MotConfigReader motCfgReader; @@ -84,13 +98,11 @@ int main(int argc, char** argv) { cr.read(&cfg, argc, argv); std::vector gtfs(cfg.feedPaths.size()); - // feed containing the shapes in memory for evaluation - ad::cppgtfs::gtfs::Feed evalFeed; std::vector cfgPaths = getCfgPaths(cfg); try { - motCfgReader.parse(cfgPaths); + motCfgReader.parse(cfgPaths, cfg.motCfgParam); } catch (const configparser::ParseExc& ex) { LOG(ERROR) << "Could not parse MOT configurations, reason was:"; std::cerr << ex.what() << std::endl; @@ -108,27 +120,24 @@ int main(int argc, char** argv) { exit(static_cast(RetCode::NO_MOT_CFG)); } + T_START(gtfsBuild); + if (cfg.feedPaths.size() == 1) { if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0]; if (!cfg.writeOverpass) - LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ..."; + LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[0] << " ..."; try { ad::cppgtfs::Parser p; p.parse(>fs[0], cfg.feedPaths[0]); - if (cfg.evaluate) { - // read the shapes and store them in memory - p.parseShapes(&evalFeed, cfg.feedPaths[0]); - } } catch (const ad::cppgtfs::ParserException& ex) { LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(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] << " ..."; + LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[i] << " ..."; ad::cppgtfs::Parser p; try { p.parse(>fs[i], cfg.feedPaths[i]); @@ -137,13 +146,14 @@ int main(int argc, char** argv) { std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_PARSE_ERR)); } - if (!cfg.writeOverpass) LOG(INFO) << "Done."; } } else if (cfg.feedPaths.size() > 1) { std::cerr << "Multiple feeds only allowed in filter mode." << std::endl; exit(static_cast(RetCode::MULT_FEEDS_NOT_ALWD)); } + auto tGtfsBuild = T_STOP(gtfsBuild); + LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size() << " unique MOT configs."; MOTs cmdCfgMots = cfg.mots; @@ -161,12 +171,20 @@ int main(int argc, char** argv) { } } + double maxSpeed = 0; + for (const auto& c : motCfgReader.getConfigs()) { + if (c.osmBuildOpts.maxSpeed > maxSpeed) { + maxSpeed = c.osmBuildOpts.maxSpeed; + } + } + if (cfg.writeOsm.size()) { LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ..."; BBoxIdx box(BOX_PADDING); + for (size_t i = 0; i < cfg.feedPaths.size(); i++) { ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true, - &box); + &box, maxSpeed); } OsmBuilder osmBuilder; std::vector opts; @@ -188,7 +206,7 @@ int main(int argc, char** argv) { BBoxIdx box(BOX_PADDING); for (size_t i = 0; i < cfg.feedPaths.size(); i++) { ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true, - &box); + &box, maxSpeed); } OsmBuilder osmBuilder; std::vector opts; @@ -205,11 +223,6 @@ int main(int argc, char** argv) { exit(static_cast(RetCode::NO_INPUT_FEED)); } - std::vector dfBins; - auto dfBinStrings = util::split(std::string(cfg.evalDfBins), ','); - for (auto st : dfBinStrings) dfBins.push_back(atof(st.c_str())); - Collector ecoll(cfg.evalPath, dfBins); - for (const auto& motCfg : motCfgReader.getConfigs()) { std::string filePost; auto usedMots = pfaedle::router::motISect(motCfg.mots, cmdCfgMots); @@ -220,7 +233,7 @@ int main(int argc, char** argv) { filePost = getFileNameMotStr(usedMots); std::string motStr = pfaedle::router::getMotStr(usedMots); - LOG(INFO) << "Calculating shapes for mots " << motStr; + LOG(INFO) << "Matching shapes for mots " << motStr; try { pfaedle::router::FeedStops fStops = @@ -231,62 +244,117 @@ int main(int argc, char** argv) { pfaedle::osm::OsmBuilder osmBuilder; pfaedle::osm::BBoxIdx box(BOX_PADDING); - ShapeBuilder::getGtfsBox(>fs[0], cmdCfgMots, cfg.shapeTripId, - cfg.dropShapes, &box); + ShapeBuilder::getGtfsBox(>fs[0], usedMots, cfg.shapeTripId, + cfg.dropShapes, &box, + motCfg.osmBuildOpts.maxSpeed); + + T_START(osmBuild); if (fStops.size()) osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box, - cfg.gridSize, &fStops, &restr); + cfg.gridSize, &restr); - // TODO(patrick): move this somewhere else - for (auto& feedStop : fStops) { - if (feedStop.second) { - feedStop.second->pl().getSI()->getGroup()->writePens( - motCfg.osmBuildOpts.trackNormzer, - motCfg.routingOpts.platformUnmatchedPen, - motCfg.routingOpts.stationDistPenFactor, - motCfg.routingOpts.nonOsmPen); - } + auto tOsmBuild = T_STOP(osmBuild); + + JaccardClassifier statsimiClassifier; + + Router* router = 0; + + if (motCfg.routingOpts.transPenMethod == "exp") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else if (motCfg.routingOpts.transPenMethod == "distdiff") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else if (motCfg.routingOpts.transPenMethod == "timenorm") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else { + LOG(ERROR) << "Unknown routing method " + << motCfg.routingOpts.transPenMethod; + exit(1); } - ShapeBuilder shapeBuilder(>fs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll, - &graph, &fStops, &restr, cfg); + ShapeBuilder shapeBuilder(>fs[0], usedMots, motCfg, &graph, &fStops, + &restr, &statsimiClassifier, router, cfg); + + pfaedle::netgraph::Graph ng; + Stats stats; + + if (singleTrip) { + 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); + + auto l = shapeBuilder.shapeL(singleTrip); + stats = l.second; + + LOG(INFO) << "Outputting path.json..."; + // reproject to WGS84 to match RFC 7946 + o.print(l.first, {}); + + o.flush(); + pstr.close(); + } else { + stats = shapeBuilder.shapeify(&ng); + } + + // outputting stats + + if (cfg.writeStats) { + size_t numEdgs = 0; + for (const auto& nd : graph.getNds()) { + numEdgs += nd->getAdjListOut().size(); + } + util::json::Dict jsonStats = { + {"statistics", + util::json::Dict{ + {"gtfs_num_stations", gtfs[0].getStops().size()}, + {"gtfs_num_trips", gtfs[0].getTrips().size()}, + {"graph_nds", graph.getNds().size()}, + {"graph_edgs", numEdgs}, + {"num_tries", stats.numTries}, + {"num_trie_leafs", stats.numTrieLeafs}, + {"dijkstra_iters", stats.dijkstraIters}, + {"time_solve", stats.solveTime}, + {"time_read_osm", tOsmBuild}, + {"time_read_gtfs", tGtfsBuild}, + {"time_tot", T_STOP(total)}, + {"peak-memory", util::readableSize(util::getPeakRSS())}, + {"peak-memory-bytes", util::getPeakRSS()}}}}; + + std::ofstream ofs; + ofs.open("stats" + filePost + ".json"); + util::json::Writer wr(&ofs, 10, true); + wr.val(jsonStats); + wr.closeAll(); + } + + if (router) delete router; 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.printLatLng(*shapeBuilder.getGraph(), fstr); + out.print(*shapeBuilder.getGraph(), fstr); fstr.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); - - auto l = shapeBuilder.shapeL(singleTrip); - - // reproject to WGS84 to match RFC 7946 - o.printLatLng(l, {}); - - o.flush(); - pstr.close(); - - exit(static_cast(RetCode::SUCCESS)); - } - - pfaedle::netgraph::Graph ng; - shapeBuilder.shape(&ng); + if (singleTrip) exit(static_cast(RetCode::SUCCESS)); 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.printLatLng(ng, fstr); + out.print(ng, fstr); fstr.close(); } } catch (const pfxml::parse_exc& ex) { @@ -296,8 +364,6 @@ int main(int argc, char** argv) { } } - if (cfg.evaluate) ecoll.printStats(&std::cout); - if (cfg.feedPaths.size()) { try { mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); @@ -305,7 +371,7 @@ int main(int argc, char** argv) { pfaedle::gtfs::Writer w; w.write(>fs[0], cfg.outputPath); } catch (const ad::cppgtfs::WriterException& ex) { - LOG(ERROR) << "Could not write final GTFS feed, reason was:"; + LOG(ERROR) << "Could not write output GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_WRITE_ERR)); } @@ -329,12 +395,10 @@ std::vector getCfgPaths(const Config& cfg) { if (cfg.configPaths.size()) return cfg.configPaths; std::vector ret; - // install prefix global configuration path, if available { - auto path = std::string(INSTALL_PREFIX) + - std::string(CFG_DIR) + "/" + "pfaedle" + "/" + - CFG_FILE_NAME; + auto path = std::string(INSTALL_PREFIX) + std::string(CFG_DIR) + "/" + + "pfaedle" + "/" + CFG_FILE_NAME; std::ifstream is(path); LOG(DEBUG) << "Testing for config file at " << path; @@ -346,8 +410,8 @@ std::vector getCfgPaths(const Config& cfg) { // local user configuration path, if available { - auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + - "pfaedle" + "/" + CFG_FILE_NAME; + auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" + + CFG_FILE_NAME; std::ifstream is(path); LOG(DEBUG) << "Testing for config file at " << path; diff --git a/src/pfaedle/config/ConfigReader.cpp b/src/pfaedle/config/ConfigReader.cpp index c9ef614..1d351e6 100644 --- a/src/pfaedle/config/ConfigReader.cpp +++ b/src/pfaedle/config/ConfigReader.cpp @@ -11,6 +11,7 @@ #include "pfaedle/_config.h" #include "pfaedle/config/ConfigReader.h" #include "util/String.h" +#include "util/geo/Geo.h" #include "util/log/Log.h" using pfaedle::config::ConfigReader; @@ -19,7 +20,7 @@ using std::string; using std::exception; using std::vector; -static const char* YEAR = __DATE__ + 7; +static const char* YEAR = &__DATE__[7]; static const char* COPY = "University of Freiburg - Chair of Algorithms and Data Structures"; static const char* AUTHORS = "Patrick Brosi "; @@ -28,7 +29,7 @@ static const char* AUTHORS = "Patrick Brosi "; void ConfigReader::help(const char* bin) { std::cout << std::setfill(' ') << std::left << "pfaedle GTFS map matcher " << VERSION_FULL << "\n(built " << __DATE__ << " " << __TIME__ - << " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n" + << " with geometry precision <" << PFDL_PREC_STR << ">)\n\n" << "(C) " << YEAR << " " << COPY << "\n" << "Authors: " << AUTHORS << "\n\n" << "Usage: " << bin @@ -43,6 +44,8 @@ void ConfigReader::help(const char* bin) { << "drop shapes already present in the feed and\n" << std::setw(35) << " " << " recalculate them\n" + << std::setw(35) << " --write-colors" + << "write matched route line colors, where missing\n" << "\nInput:\n" << std::setw(35) << " -c [ --config ] arg" << "pfaedle config file\n" @@ -83,26 +86,6 @@ void ConfigReader::help(const char* bin) { << "write routing graph as GeoJSON to\n" << std::setw(35) << " " << " /graph.json\n" - << std::setw(35) << " --write-cgraph" - << "if -T is set, write combination graph as\n" - << std::setw(35) << " " - << " GeoJSON to " - "/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 , write result \n" @@ -111,11 +94,19 @@ void ConfigReader::help(const char* bin) { << 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"; + << "Approx. grid cell size in meters\n" + << std::setw(35) << " --no-fast-hops" + << "Disable fast hops technique\n" + << std::setw(35) << " --no-a-star" + << "Disable A* heuristic \n" + << std::setw(35) << " --no-trie" + << "Disable trip tries \n" + << std::setw(35) << " --no-hop-cache" + << "Disable hop cache \n" + << std::setw(35) << " --stats" + << "write stats to stats.json\n" + << std::setw(35) << " -P" + << "additional parameter string (in cfg file format)\n"; } // _____________________________________________________________________________ @@ -134,46 +125,37 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { {"osm-out", required_argument, 0, 'X'}, {"trip-id", required_argument, 0, 'T'}, {"write-graph", no_argument, 0, 1}, - {"write-cgraph", no_argument, 0, 2}, {"write-trgraph", no_argument, 0, 4}, - {"method", required_argument, 0, 5}, - {"eval", no_argument, 0, 3}, - {"eval-path", required_argument, 0, 6}, - {"eval-df-bins", required_argument, 0, 7}, {"dbg-path", required_argument, 0, 'd'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"inplace", no_argument, 0, 9}, - {"use-route-cache", no_argument, 0, 8}, + {"no-fast-hops", no_argument, 0, 10}, + {"no-a-star", no_argument, 0, 11}, + {"no-trie", no_argument, 0, 12}, + {"write-colors", no_argument, 0, 13}, + {"stats", no_argument, 0, 14}, + {"no-hop-cache", no_argument, 0, 15}, {0, 0, 0, 0}}; char c; - while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:p", ops, 0)) != + while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:pP:", ops, 0)) != -1) { switch (c) { case 1: cfg->writeGraph = true; break; - case 2: - cfg->writeCombGraph = true; - break; - case 3: - cfg->evaluate = true; - break; case 4: cfg->buildTransitGraph = true; break; - case 5: - cfg->solveMethod = optarg; + case 10: + cfg->noFastHops = true; break; - case 6: - cfg->evalPath = optarg; + case 11: + cfg->noAStar = true; break; - case 7: - cfg->evalDfBins = optarg; - break; - case 8: - cfg->useCaching = true; + case 12: + cfg->noTrie = true; break; case 'o': cfg->outputPath = optarg; @@ -194,7 +176,7 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { motStr = optarg; break; case 'g': - cfg->gridSize = atof(optarg); + cfg->gridSize = atof(optarg) / util::geo::M_PER_DEG; break; case 'X': cfg->writeOsm = optarg; @@ -202,6 +184,9 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { case 'T': cfg->shapeTripId = optarg; break; + case 'P': + cfg->motCfgParam += std::string("\n") + optarg; + break; case 'd': cfg->dbgOutputPath = optarg; break; @@ -211,10 +196,19 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { case 9: cfg->inPlace = true; break; + case 13: + cfg->writeColors = true; + break; + case 14: + cfg->writeStats = true; + break; + case 15: + cfg->noHopCache = true; + break; case 'v': std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << " with geometry precision <" - << PFAEDLE_PRECISION_STR << ">)\n" + << PFDL_PREC_STR << ">)\n" << "(C) " << YEAR << " " << COPY << "\n" << "Authors: " << AUTHORS << "\nGNU General Public " "License v3.0\n"; diff --git a/src/pfaedle/config/MotConfig.h b/src/pfaedle/config/MotConfig.h index 3b7ab48..f99f4eb 100644 --- a/src/pfaedle/config/MotConfig.h +++ b/src/pfaedle/config/MotConfig.h @@ -17,20 +17,11 @@ struct MotConfig { router::MOTs mots; osm::OsmReadOpts osmBuildOpts; router::RoutingOpts routingOpts; - std::map unproced; + std::string transWeight; }; inline bool operator==(const MotConfig& a, const MotConfig& b) { - bool unprocedEq = a.unproced.size() == b.unproced.size(); - for (const auto& kv : a.unproced) { - if (!b.unproced.count(kv.first) || - b.unproced.find(kv.first)->second != kv.second) { - unprocedEq = false; - break; - } - } - return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts && - unprocedEq; + return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts; } } // namespace config diff --git a/src/pfaedle/config/MotConfigReader.cpp b/src/pfaedle/config/MotConfigReader.cpp index 96fce0e..bf1b0cd 100644 --- a/src/pfaedle/config/MotConfigReader.cpp +++ b/src/pfaedle/config/MotConfigReader.cpp @@ -2,28 +2,33 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi +#include #include #include #include "pfaedle/config/MotConfigReader.h" +#include "pfaedle/osm/OsmReadOpts.h" #include "util/Misc.h" #include "util/String.h" #include "util/log/Log.h" -using pfaedle::config::MotConfigReader; -using pfaedle::config::MotConfig; -using pfaedle::osm::FilterRule; -using pfaedle::osm::KeyVal; +using ad::cppgtfs::gtfs::Route; using configparser::ConfigFileParser; using configparser::ParseExc; +using pfaedle::config::MotConfig; +using pfaedle::config::MotConfigReader; using pfaedle::osm::DeepAttrRule; +using pfaedle::osm::FilterRule; +using pfaedle::osm::KeyVal; using pfaedle::trgraph::ReplRules; -using ad::cppgtfs::gtfs::Route; + +double DEF_TRANS_PEN = 0.0083; // _____________________________________________________________________________ MotConfigReader::MotConfigReader() {} // _____________________________________________________________________________ -void MotConfigReader::parse(const std::vector& paths) { +void MotConfigReader::parse(const std::vector& paths, + const std::string& literal) { ConfigFileParser p; // parse explicitely given paths @@ -32,17 +37,33 @@ void MotConfigReader::parse(const std::vector& paths) { p.parse(s); } + if (literal.size()) p.parseStr(literal); + for (const auto& sec : p.getSecs()) { - MotConfig curCfg; + MotConfig cfg; + + cfg.transWeight = "expo"; + std::string secStr = sec.first; if (secStr.empty()) continue; - std::set procedKeys; + + if (p.hasKey(secStr, "routing_transition_method")) { + cfg.routingOpts.transPenMethod = + p.getStr(secStr, "routing_transition_method"); + } else { + cfg.routingOpts.transPenMethod = "exp"; + } + + if (p.hasKey(secStr, "routing_use_stations")) { + cfg.routingOpts.useStations = p.getBool(secStr, "routing_use_stations"); + } else { + cfg.routingOpts.useStations = true; + } if (p.hasKey(secStr, "osm_filter_keep")) { - procedKeys.insert("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( + cfg.osmBuildOpts.keepFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } @@ -50,321 +71,471 @@ void MotConfigReader::parse(const std::vector& paths) { 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)) { - procedKeys.insert(name); for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert( + cfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } } if (p.hasKey(secStr, "osm_filter_drop")) { - procedKeys.insert("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( + cfg.osmBuildOpts.dropFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_max_snap_level")) { - procedKeys.insert("osm_max_snap_level"); - curCfg.osmBuildOpts.maxSnapLevel = - p.getInt(sec.first, "osm_max_snap_level"); + cfg.osmBuildOpts.maxSnapLevel = p.getInt(sec.first, "osm_max_snap_level"); } else { - curCfg.osmBuildOpts.maxSnapLevel = 7; + cfg.osmBuildOpts.maxSnapLevel = 7; } if (p.hasKey(secStr, "osm_filter_nohup")) { - procedKeys.insert("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( + cfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_oneway")) { - procedKeys.insert("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( + cfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_oneway_reverse")) { - procedKeys.insert("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( + cfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_undirected")) { - procedKeys.insert("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( + cfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_station")) { - procedKeys.insert("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( + cfg.osmBuildOpts.stationFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_station_blocker")) { - procedKeys.insert("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( + cfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert( + osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); + } + } + + if (p.hasKey(secStr, "osm_filter_turning_cycle")) { + for (const auto& kvs : + p.getStrArr(sec.first, "osm_filter_turning_cycle", ' ')) { + auto fRule = getFRule(kvs); + cfg.osmBuildOpts.turnCycleFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_node_positive_restriction")) { - procedKeys.insert("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( + cfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_node_negative_restriction")) { - procedKeys.insert("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( + cfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_no_restriction")) { - procedKeys.insert("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( + cfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_station_name_attrs")) { - procedKeys.insert("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)); + cfg.osmBuildOpts.statAttrRules.nameRule.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_track_number_tags")) { - procedKeys.insert("osm_track_number_tags"); for (const std::string& r : p.getStrArr(sec.first, "osm_track_number_tags", ' ')) { - curCfg.osmBuildOpts.statAttrRules.platformRule.push_back( + cfg.osmBuildOpts.statAttrRules.platformRule.push_back( getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_station_id_attrs")) { - procedKeys.insert("osm_station_id_attrs"); for (const std::string& r : p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) { - curCfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r)); + cfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_edge_track_number_tags")) { - procedKeys.insert("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)); + cfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_station_group_attrs")) { - procedKeys.insert("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}); - } + LOG(WARN) << "Option osm_station_group_attrs has been removed."; } + // default value, to enable color writing on old configs + cfg.osmBuildOpts.relLinerules.colorRule = {"colour", "color"}; + if (p.hasKey(secStr, "osm_line_relation_tags")) { - procedKeys.insert("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; + cfg.osmBuildOpts.relLinerules.fromNameRule = tags; else if (rule.first == "to_name") - curCfg.osmBuildOpts.relLinerules.toNameRule = tags; + cfg.osmBuildOpts.relLinerules.toNameRule = tags; else if (rule.first == "line_name") - curCfg.osmBuildOpts.relLinerules.sNameRule = tags; + cfg.osmBuildOpts.relLinerules.sNameRule = tags; + else if (rule.first == "line_color") + cfg.osmBuildOpts.relLinerules.colorRule = tags; } } + cfg.osmBuildOpts.maxSnapDistance = 50; if (p.hasKey(secStr, "osm_max_snap_distance")) { - procedKeys.insert("osm_max_snap_distance"); - curCfg.osmBuildOpts.maxSnapDistances = - p.getDoubleArr(secStr, "osm_max_snap_distance", ','); - } else { - curCfg.osmBuildOpts.maxSnapDistances.push_back(50); + auto v = p.getDoubleArr(secStr, "osm_max_snap_distance", ','); + if (v.size()) cfg.osmBuildOpts.maxSnapDistance = v.back(); + } + + cfg.osmBuildOpts.maxStationCandDistance = + cfg.osmBuildOpts.maxSnapDistance * 2; + if (p.hasKey(secStr, "osm_max_station_cand_distance")) { + auto v = p.getDouble(secStr, "osm_max_station_cand_distance"); + cfg.osmBuildOpts.maxStationCandDistance = v; } if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) { - procedKeys.insert("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; + LOG(WARN) << "Option osm_max_snap_fallback_distance has been removed."; } if (p.hasKey(secStr, "osm_max_osm_station_distance")) { - procedKeys.insert("osm_max_osm_station_distance"); - curCfg.osmBuildOpts.maxOsmStationDistance = - p.getDouble(secStr, "osm_max_osm_station_distance"); + double ref = p.getDouble(secStr, "osm_max_osm_station_distance"); + cfg.osmBuildOpts.maxOsmStationDistances.push_back(fmin(5, ref)); + for (double i = 10; i < ref + 1; i += 10) { + cfg.osmBuildOpts.maxOsmStationDistances.push_back(i); + } } else { - curCfg.osmBuildOpts.maxOsmStationDistance = 5; + cfg.osmBuildOpts.maxOsmStationDistances.push_back(5); } if (p.hasKey(secStr, "osm_max_node_block_distance")) { - procedKeys.insert("osm_max_node_block_distance"); - curCfg.osmBuildOpts.maxBlockDistance = + cfg.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()) / + cfg.osmBuildOpts.maxBlockDistance = + *std::max_element(cfg.osmBuildOpts.maxOsmStationDistances.begin(), + cfg.osmBuildOpts.maxOsmStationDistances.end()) / 8; } + double DEF_SPEED = 85; 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)) { - procedKeys.insert(name); - double v = p.getDouble(sec.first, name); - curCfg.routingOpts.levelPunish[i] = v; - } else { - curCfg.routingOpts.levelPunish[i] = 1; + double f = p.getPosDouble(sec.first, name); + LOG(WARN) << "Option " << name << " is deprecated, use osm_lvl" + << std::to_string(i) << "_avg_speed instead."; + double v = DEF_SPEED / f; + LOG(DEBUG) << " (using osm_lvl" << std::to_string(i) << "_avg_speed of " + << v << " instead)"; + cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s } } - if (p.hasKey(secStr, "routing_full_turn_punish")) { - procedKeys.insert("routing_full_turn_punish"); - curCfg.routingOpts.fullTurnPunishFac = - p.getDouble(secStr, "routing_full_turn_punish"); - } - - if (p.hasKey(secStr, "routing_no_self_hops")) { - procedKeys.insert("routing_no_self_hops"); - curCfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops"); - } - - if (p.hasKey(secStr, "routing_full_turn_angle")) { - procedKeys.insert("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")) { - procedKeys.insert("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")) { - procedKeys.insert("routing_pass_thru_station_punish"); - curCfg.routingOpts.passThruStationsPunish = - p.getDouble(secStr, "routing_pass_thru_station_punish"); + for (uint8_t i = 0; i < 8; i++) { + std::string name = + std::string("osm_lvl") + std::to_string(i) + "_avg_speed"; + if (p.hasKey(secStr, name)) { + double v = p.getPosDouble(sec.first, name); + cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s + } } if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) { - procedKeys.insert("routing_one_way_meter_punish_fac"); - curCfg.routingOpts.oneWayPunishFac = - p.getDouble(secStr, "routing_one_way_meter_punish_fac"); + LOG(WARN) << "Option routing_one_way_meter_punish_fac is deprecated, use " + "osm_one_way_speed_penalty_fac instead."; + cfg.osmBuildOpts.oneWaySpeedPen = + 1 + p.getPosDouble(secStr, "routing_one_way_meter_punish_fac"); + LOG(DEBUG) << " (using osm_one_way_speed_penalty_fac of " + << cfg.osmBuildOpts.oneWaySpeedPen << " instead)"; + } else { + cfg.osmBuildOpts.oneWaySpeedPen = 1; } - if (p.hasKey(secStr, "routing_one_way_edge_punish")) { - procedKeys.insert("routing_one_way_edge_punish"); - curCfg.routingOpts.oneWayEdgePunish = - p.getDouble(secStr, "routing_one_way_edge_punish"); + if (p.hasKey(secStr, "osm_one_way_speed_penalty_fac")) { + cfg.osmBuildOpts.oneWaySpeedPen = + p.getPosDouble(secStr, "osm_one_way_speed_penalty_fac"); + } else { + // def already set above } - if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) { - procedKeys.insert("routing_line_unmatched_punish_fac"); - curCfg.routingOpts.lineUnmatchedPunishFact = - p.getDouble(secStr, "routing_line_unmatched_punish_fac"); + if (p.hasKey(secStr, "osm_one_way_entry_cost")) { + cfg.osmBuildOpts.oneWayEntryCost = + p.getPosDouble(secStr, "osm_one_way_entry_cost"); + + } else { + cfg.osmBuildOpts.oneWayEntryCost = 0; } + // take the same cost for taking restricted turns to keep + // configuration simple + double val = cfg.osmBuildOpts.oneWayEntryCost * 10.0; + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.turnRestrCost = val; + + if (p.hasKey(secStr, "routing_full_turn_punish")) { + double val = p.getPosDouble(secStr, "routing_full_turn_punish"); + + LOG(WARN) << "Option routing_full_turn_punish is deprecated, use " + "routing_full_turn_penalty instead."; + + val /= cfg.osmBuildOpts.levelDefSpeed[0]; + + LOG(DEBUG) << " (using routing_full_turn_penalty of " << val + << " instead)"; + + val *= 10.0; + + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.fullTurnPunishFac = val; + } + + if (p.hasKey(secStr, "routing_full_turn_penalty")) { + double val = p.getPosDouble(secStr, "routing_full_turn_penalty") * 10.0; + + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.fullTurnPunishFac = val; + } + + if (p.hasKey(secStr, "routing_no_self_hops")) { + cfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops"); + } + + if (p.hasKey(secStr, "routing_full_turn_angle")) { + double ang = p.getPosDouble(secStr, "routing_full_turn_angle"); + cfg.routingOpts.fullTurnAngle = ang; + cfg.osmBuildOpts.fullTurnAngle = ang; + } else { + cfg.routingOpts.fullTurnAngle = 5; + cfg.osmBuildOpts.fullTurnAngle = 5; + } + + if (p.hasKey(secStr, "routing_snap_full_turn_angle")) { + double ang = p.getPosDouble(secStr, "routing_snap_full_turn_angle"); + cfg.osmBuildOpts.maxAngleSnapReach = ang; + } else { + cfg.osmBuildOpts.maxAngleSnapReach = cfg.routingOpts.fullTurnAngle; + } + + if (p.hasKey(secStr, "routing_pass_thru_station_punish")) { + LOG(WARN) << "Option routing_pass_thru_station_punish has been removed."; + } + + cfg.routingOpts.turnRestrCost *= 10.0; + if (p.hasKey(secStr, "routing_no_lines_punish_fac")) { - procedKeys.insert("routing_no_lines_punish_fac"); - curCfg.routingOpts.noLinesPunishFact = - p.getDouble(secStr, "routing_no_lines_punish_fac"); + LOG(WARN) << "Option routing_no_lines_punish_fac is deprecated, use " + "routing_no_lines_penalty_fac instead."; + + cfg.routingOpts.noLinesPunishFact = + 1 + p.getPosDouble(secStr, "routing_no_lines_punish_fac"); + + LOG(DEBUG) << " (using routing_no_lines_penalty_fac of " + << cfg.routingOpts.noLinesPunishFact << " instead)"; + } else { + cfg.routingOpts.noLinesPunishFact = 1; + } + + if (p.hasKey(secStr, "routing_no_lines_penalty_fac")) { + cfg.routingOpts.noLinesPunishFact = + p.getPosDouble(secStr, "routing_no_lines_penalty_fac"); + } else { + // default already set above + } + + // store this at two places, as we are writing the punishment into the graph + cfg.osmBuildOpts.noLinesPunishFact = cfg.routingOpts.noLinesPunishFact; + + if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) { + LOG(WARN) + << "Option routing_line_unmatched_punish_fac is deprecated, use " + "routing_line_unmatched_time_penalty_fac, " + "routing_line_station_from_unmatched_time_penalty, and " + "routing_line_station_to_unmatched_time_penalty instead."; + + cfg.routingOpts.lineUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + cfg.routingOpts.lineNameFromUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + cfg.routingOpts.lineNameToUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + LOG(DEBUG) << " (using routing_line_unmatched_punish_fac of " + << cfg.routingOpts.lineUnmatchedPunishFact << " instead)"; + LOG(DEBUG) + << " (using routing_line_station_from_unmatched_time_penalty of " + << cfg.routingOpts.lineNameFromUnmatchedPunishFact << " instead)"; + LOG(DEBUG) << " (using routing_line_station_to_unmatched_time_penalty of " + << cfg.routingOpts.lineNameToUnmatchedPunishFact + << " instead)"; + } + + if (p.hasKey(secStr, "routing_line_unmatched_time_penalty_fac")) { + cfg.routingOpts.lineUnmatchedPunishFact = + p.getPosDouble(secStr, "routing_line_unmatched_time_penalty_fac"); + } + + if (p.hasKey(secStr, "routing_line_station_from_unmatched_time_penalty")) { + cfg.routingOpts.lineNameFromUnmatchedPunishFact = p.getPosDouble( + secStr, "routing_line_station_from_unmatched_time_penalty"); + } + + if (p.hasKey(secStr, "routing_line_station_to_unmatched_time_penalty")) { + cfg.routingOpts.lineNameToUnmatchedPunishFact = p.getPosDouble( + secStr, "routing_line_station_to_unmatched_time_penalty"); } if (p.hasKey(secStr, "routing_platform_unmatched_punish")) { - procedKeys.insert("routing_platform_unmatched_punish"); - curCfg.routingOpts.platformUnmatchedPen = - p.getDouble(secStr, "routing_platform_unmatched_punish"); + LOG(WARN) + << "Option routing_platform_unmatched_punish is deprecated, use " + "routing_platform_unmatched_penalty instead."; + cfg.routingOpts.platformUnmatchedPen = + p.getPosDouble(secStr, "routing_platform_unmatched_punish"); + + cfg.routingOpts.platformUnmatchedPen = + cfg.routingOpts.platformUnmatchedPen * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + + LOG(DEBUG) << " (using routing_platform_unmatched_penalty of " + << cfg.routingOpts.platformUnmatchedPen << " instead)"; + } else { + cfg.routingOpts.platformUnmatchedPen = 0; } - if (p.hasKey(secStr, "routing_non_osm_station_punish")) { - procedKeys.insert("routing_non_osm_station_punish"); - curCfg.routingOpts.nonOsmPen = - p.getDouble(secStr, "routing_non_osm_station_punish"); + if (p.hasKey(secStr, "routing_platform_unmatched_penalty")) { + cfg.routingOpts.platformUnmatchedPen = + p.getPosDouble(secStr, "routing_platform_unmatched_penalty"); } else { - curCfg.routingOpts.nonOsmPen = 0; + // default already set above + } + + if (p.hasKey(secStr, "routing_transition_penalty_fac")) { + cfg.routingOpts.transitionPen = + p.getPosDouble(secStr, "routing_transition_penalty_fac"); + } else { + cfg.routingOpts.transitionPen = DEF_TRANS_PEN; } if (p.hasKey(secStr, "routing_station_distance_punish_fac")) { - procedKeys.insert("routing_station_distance_punish_fac"); - curCfg.routingOpts.stationDistPenFactor = - p.getDouble(secStr, "routing_station_distance_punish_fac"); + cfg.routingOpts.stationDistPenFactor = + p.getPosDouble(secStr, "routing_station_distance_punish_fac"); + LOG(WARN) << "Option routing_station_distance_punish_fac is deprecated, " + "use routing_station_move_penalty_fac instead."; + cfg.routingOpts.stationDistPenFactor = + cfg.routingOpts.stationDistPenFactor * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + LOG(DEBUG) << " (using routing_station_move_penalty_fac of " + << cfg.routingOpts.stationDistPenFactor << " instead)"; } else { - curCfg.routingOpts.stationDistPenFactor = 1; + cfg.routingOpts.stationDistPenFactor = + cfg.routingOpts.stationDistPenFactor * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + } + + if (p.hasKey(secStr, "routing_station_move_penalty_fac")) { + cfg.routingOpts.stationDistPenFactor = + p.getPosDouble(secStr, "routing_station_move_penalty_fac"); + } else { + // the default value was already set above + } + + if (p.hasKey(secStr, "routing_non_osm_station_punish")) { + cfg.routingOpts.nonStationPen = + p.getPosDouble(secStr, "routing_non_osm_station_punish"); + LOG(WARN) << "Option routing_non_osm_station_punish is deprecated, use " + "routing_non_station_penalty instead."; + cfg.routingOpts.nonStationPen = + cfg.routingOpts.nonStationPen * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + LOG(DEBUG) << " (using routing_non_station_penalty of " + << cfg.routingOpts.nonStationPen << " instead)"; + } else { + cfg.routingOpts.nonStationPen = 0; + } + + if (p.hasKey(secStr, "routing_non_station_penalty")) { + cfg.routingOpts.nonStationPen = + p.getPosDouble(secStr, "routing_non_station_penalty"); + } else { + // default was already set above + } + + if (p.hasKey(secStr, "routing_station_unmatched_penalty")) { + cfg.routingOpts.stationUnmatchedPen = + p.getPosDouble(secStr, "routing_station_unmatched_penalty"); + } else { + cfg.routingOpts.stationUnmatchedPen = cfg.routingOpts.nonStationPen / 2; } if (p.hasKey(secStr, "station_normalize_chain")) { - procedKeys.insert("station_normalize_chain"); try { auto arr = p.getStrArr(secStr, "station_normalize_chain", ';'); - curCfg.osmBuildOpts.statNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.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, @@ -375,11 +546,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "track_normalize_chain")) { - procedKeys.insert("track_normalize_chain"); try { auto arr = p.getStrArr(secStr, "track_normalize_chain", ';'); - curCfg.osmBuildOpts.trackNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.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, @@ -390,11 +559,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "line_normalize_chain")) { - procedKeys.insert("line_normalize_chain"); try { auto arr = p.getStrArr(secStr, "line_normalize_chain", ';'); - curCfg.osmBuildOpts.lineNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.lineNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "line_normalize_chain").line, p.getVal(secStr, "line_normalize_chain").pos, @@ -405,10 +572,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "station_id_normalize_chain")) { - procedKeys.insert("station_id_normalize_chain"); try { auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';'); - curCfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "station_id_normalize_chain").line, p.getVal(secStr, "station_id_normalize_chain").pos, @@ -418,18 +584,41 @@ void MotConfigReader::parse(const std::vector& paths) { } } - for (const auto& kv : p.getKeyVals(secStr)) { - if (!procedKeys.count(kv.first)) - curCfg.unproced[kv.first] = kv.second.val; + // determine the maximum possible speed for this config, this is later + // used to filter out station which are so far out of reach we don't + // have to consider them for the bounding box calculation + cfg.osmBuildOpts.maxSpeed = 0; + cfg.osmBuildOpts.maxSpeedCorFac = 1; + for (size_t i = 0; i < 8; i++) { + if (cfg.osmBuildOpts.levelDefSpeed[i] > cfg.osmBuildOpts.maxSpeed) + cfg.osmBuildOpts.maxSpeed = cfg.osmBuildOpts.levelDefSpeed[i]; } + if (cfg.routingOpts.lineUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineUnmatchedPunishFact; + if (cfg.routingOpts.lineNameFromUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineNameFromUnmatchedPunishFact; + if (cfg.routingOpts.lineNameToUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineNameToUnmatchedPunishFact; + + if (cfg.routingOpts.noLinesPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= cfg.routingOpts.noLinesPunishFact; + + if (cfg.osmBuildOpts.oneWaySpeedPen < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= cfg.osmBuildOpts.oneWaySpeedPen; + + cfg.osmBuildOpts.maxSpeed /= cfg.osmBuildOpts.maxSpeedCorFac; + bool found = false; - for (auto& cfg : _cfgs) { - if (cfg == curCfg) { + for (auto& exCfg : _cfgs) { + if (cfg == exCfg) { for (auto mot : ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) { - cfg.mots.insert(mot); + exCfg.mots.insert(mot); } found = true; break; @@ -437,8 +626,8 @@ void MotConfigReader::parse(const std::vector& paths) { } if (!found) { - curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr); - _cfgs.push_back(curCfg); + cfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr); + _cfgs.push_back(cfg); } } } diff --git a/src/pfaedle/config/MotConfigReader.h b/src/pfaedle/config/MotConfigReader.h index 51716ff..86e3ffb 100644 --- a/src/pfaedle/config/MotConfigReader.h +++ b/src/pfaedle/config/MotConfigReader.h @@ -23,7 +23,7 @@ using ad::cppgtfs::gtfs::Route; class MotConfigReader { public: MotConfigReader(); - void parse(const std::vector& paths); + void parse(const std::vector& paths, const std::string& literal); const std::vector& getConfigs() const; diff --git a/src/pfaedle/config/PfaedleConfig.h b/src/pfaedle/config/PfaedleConfig.h index e5bc41a..feaa645 100644 --- a/src/pfaedle/config/PfaedleConfig.h +++ b/src/pfaedle/config/PfaedleConfig.h @@ -5,10 +5,11 @@ #ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_ #define PFAEDLE_CONFIG_PFAEDLECONFIG_H_ +#include #include #include #include -#include +#include "util/geo/Geo.h" #include "ad/cppgtfs/gtfs/Route.h" namespace pfaedle { @@ -20,38 +21,44 @@ struct Config { Config() : dbgOutputPath("."), solveMethod("global"), - evalPath("."), outputPath("gtfs-out"), dropShapes(false), useHMM(false), writeGraph(false), - writeCombGraph(false), - evaluate(false), buildTransitGraph(false), useCaching(false), writeOverpass(false), inPlace(false), - gridSize(2000) {} + writeColors(false), + noFastHops(false), + noAStar(false), + noTrie(false), + noHopCache(false), + writeStats(false), + gridSize(2000 / util::geo::M_PER_DEG) {} std::string dbgOutputPath; std::string solveMethod; - std::string evalPath; std::string shapeTripId; std::string outputPath; std::string writeOsm; std::string osmPath; - std::string evalDfBins; + std::string motCfgParam; std::vector feedPaths; std::vector configPaths; std::set mots; bool dropShapes; bool useHMM; bool writeGraph; - bool writeCombGraph; - bool evaluate; bool buildTransitGraph; bool useCaching; bool writeOverpass; bool inPlace; + bool writeColors; + bool noFastHops; + bool noAStar; + bool noTrie; + bool noHopCache; + bool writeStats; double gridSize; std::string toString() { @@ -64,10 +71,16 @@ struct Config { << "drop-shapes: " << dropShapes << "\n" << "use-hmm: " << useHMM << "\n" << "write-graph: " << writeGraph << "\n" - << "write-cgraph: " << writeCombGraph << "\n" << "grid-size: " << gridSize << "\n" << "use-cache: " << useCaching << "\n" << "write-overpass: " << writeOverpass << "\n" + << "inplace: " << inPlace << "\n" + << "write-colors: " << writeColors << "\n" + << "no-fast-hops: " << noFastHops << "\n" + << "no-a-star: " << noAStar << "\n" + << "no-trie: " << noTrie << "\n" + << "no-hop-cache: " << noHopCache << "\n" + << "write-stats: " << writeStats << "\n" << "feed-paths: "; for (const auto& p : feedPaths) { diff --git a/src/pfaedle/eval/Collector.cpp b/src/pfaedle/eval/Collector.cpp deleted file mode 100644 index 709830b..0000000 --- a/src/pfaedle/eval/Collector.cpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include -#include -#include -#include -#include -#include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/Def.h" -#include "pfaedle/eval/Collector.h" -#include "pfaedle/eval/Result.h" -#include "util/geo/Geo.h" -#include "util/geo/PolyLine.h" -#include "util/geo/output/GeoJsonOutput.h" -#include "util/log/Log.h" - -using util::geo::PolyLine; - -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, - const std::vector& newTripDists) { - if (!oldS) { - _noOrigShp++; - return 0; - } - - for (auto st : t->getStopTimes()) { - if (st.getShapeDistanceTravelled() < 0) { - // we cannot safely compare trips without shape dist travelled - // info - _noOrigShp++; - return 0; - } - } - - double fd = 0; - size_t unmatchedSegments; - double unmatchedSegmentsLength; - - std::vector oldDists; - LINE oldL = getWebMercLine( - oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(), - (--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists); - - std::vector newDists; - LINE newL = getWebMercLine(&newS, -1, -1, &newDists); - - std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json"); - GeoJsonOutput gjout(fstr); - - auto oldSegs = segmentize(t, oldL, oldDists, 0); - auto newSegs = segmentize(t, newL, newDists, &newTripDists); - - // cut both result at the beginning and end to clear evaluation from - // loops at the end - POLYLINE oldStart = oldSegs[0]; - POLYLINE newStart = newSegs[0]; - auto oldStartNew = - oldStart.getSegment(oldStart.projectOn(newSegs[0][0]).totalPos, 1); - auto newStartNew = - newStart.getSegment(newStart.projectOn(oldSegs[0][0]).totalPos, 1); - if (fabs(oldStartNew.getLength() - oldStart.getLength()) / - oldStart.getLength() < - 0.5 && - fabs(newStartNew.getLength() - newStart.getLength()) / - newStart.getLength() < - 0.5) { - oldSegs[0] = oldStartNew.getLine(); - newSegs[0] = newStartNew.getLine(); - } - - POLYLINE oldEnd = oldSegs[oldSegs.size() - 1]; - POLYLINE newEnd = newSegs[oldSegs.size() - 1]; - auto oldEndNew = - oldEnd.getSegment(0, oldEnd.projectOn(newSegs.back().back()).totalPos); - auto newEndNew = - newEnd.getSegment(0, newEnd.projectOn(oldSegs.back().back()).totalPos); - if (fabs(oldEndNew.getLength() - oldEnd.getLength()) / oldEnd.getLength() < - 0.5 && - fabs(newEndNew.getLength() - newEnd.getLength()) / newEnd.getLength() < - 0.5) { - oldSegs[oldSegs.size() - 1] = oldEndNew.getLine(); - newSegs[newSegs.size() - 1] = newEndNew.getLine(); - } - - // check for suspicious (most likely erroneous) lines in the - // ground truth data which have a long straight-line segment - - for (auto oldL : oldSegs) { - for (size_t i = 1; i < oldL.size(); i++) { - if (util::geo::webMercMeterDist(oldL[i - 1], oldL[i]) > 500) { - // return 0; - } - } - } - - // new lines build from cleaned-up shapes - LINE oldLCut; - LINE newLCut; - - for (auto oldL : oldSegs) { - gjout.printLatLng(oldL, util::json::Dict{{"ver", "old"}}); - oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); - } - - for (auto newL : newSegs) { - gjout.printLatLng(newL, util::json::Dict{{"ver", "new"}}); - newLCut.insert(newLCut.end(), newL.begin(), newL.end()); - } - - gjout.flush(); - fstr.close(); - - double fac = cos(2 * atan(exp((oldSegs.front().front().getY() + - oldSegs.back().back().getY()) / - 6378137.0)) - - 1.5707965); - - 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.getId()] = fd; - } - - 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.getId()] = dA; - unmatchedSegments = dA.first; - unmatchedSegmentsLength = dA.second; - } - - double totL = 0; - for (auto l : oldSegs) totL += util::geo::len(l) * fac; - - // filter out shapes with a lenght of under 5 meters - they are most likely - // artifacts - if (totL < 5) { - _noOrigShp++; - return 0; - } - - _fdSum += fd / totL; - _unmatchedSegSum += unmatchedSegments; - _unmatchedSegLengthSum += unmatchedSegmentsLength; - _results.insert(Result(t, fd / totL)); - _resultsAN.insert(Result(t, static_cast(unmatchedSegments) / - static_cast(oldSegs.size()))); - _resultsAL.insert(Result(t, unmatchedSegmentsLength / totL)); - - LOG(DEBUG) << "This result (" << t->getId() - << "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size() - << " = " - << static_cast(unmatchedSegments) / - static_cast(oldSegs.size()) - << " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = " - << unmatchedSegmentsLength / totL << " d_f = " << fd; - - return fd; -} - -// _____________________________________________________________________________ -std::vector Collector::segmentize( - const Trip* t, const LINE& shape, const std::vector& dists, - const std::vector* newTripDists) { - std::vector ret; - - if (t->getStopTimes().size() < 2) return ret; - - POLYLINE pl(shape); - std::vector > cuts; - - size_t i = 0; - for (auto st : t->getStopTimes()) { - if (newTripDists) { - cuts.push_back(std::pair( - util::geo::latLngToWebMerc(st.getStop()->getLat(), - st.getStop()->getLng()), - (*newTripDists)[i])); - } else { - cuts.push_back(std::pair( - util::geo::latLngToWebMerc(st.getStop()->getLat(), - st.getStop()->getLng()), - st.getShapeDistanceTravelled())); - } - i++; - } - - // get first half of geometry, and search for start point there! - size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) - - dists.begin(); - 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); - - for (size_t i = 1; i < cuts.size(); i++) { - size_t before = shape.size(); - if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) { - before = - std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) - - dists.begin(); - } - - POLYLINE beforePl(LINE(shape.begin(), shape.begin() + before)); - - auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex); - - ret.push_back(pl.getSegment(lastLp, curLp).getLine()); - lastLp = curLp; - } - - // std::raise(SIGABRT); - return ret; -} - -// _____________________________________________________________________________ -LINE Collector::getWebMercLine(const Shape* s, double from, double t) { - return getWebMercLine(s, from, t, 0); -} - -// _____________________________________________________________________________ -LINE Collector::getWebMercLine(const Shape* s, double from, double to, - std::vector* dists) { - LINE ret; - - auto i = s->getPoints().begin(); - - for (; i != s->getPoints().end(); i++) { - auto p = *i; - - if ((from < 0 || (p.travelDist - from) > -0.01)) { - if (to >= 0 && (p.travelDist - to) > 0.01) break; - - POINT mercP = util::geo::latLngToWebMerc(p.lat, p.lng); - - ret.push_back(mercP); - if (dists) dists->push_back(p.travelDist); - } - } - - return ret; -} - -// _____________________________________________________________________________ -const std::set& Collector::getResults() const { return _results; } - -// _____________________________________________________________________________ -double Collector::getAvgDist() const { return _fdSum / _results.size(); } - -// _____________________________________________________________________________ -void Collector::printHisto(std::ostream* os, const std::set& result, - const std::vector& bins) const { - size_t W = 60; - - auto it = result.begin(); - std::vector > res; - std::vector examples; - size_t maxC = 0; - - for (size_t i = 0; i < bins.size(); i++) { - size_t c = 0; - const Trip* trip = 0; - - while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) { - if (!trip) trip = it->getTrip(); - c++; - it++; - } - - if (c > maxC) maxC = c; - - examples.push_back(trip); - res.push_back(std::pair(bins[i], c)); - } - - size_t j = 0; - for (auto r : res) { - std::string range = util::toString(r.first); - (*os) << " < " << std::setfill(' ') << std::setw(10) << range << ": "; - size_t i = 0; - - for (; i < W * (static_cast(r.second) / static_cast(maxC)); - i++) { - (*os) << "|"; - } - - if (r.second) - (*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")"; - (*os) << std::endl; - j++; - } -} - -// _____________________________________________________________________________ -std::vector Collector::getBins(double mind, double maxd, size_t steps) { - double bin = (maxd - mind) / steps; - double curE = mind + bin; - - std::vector ret; - while (curE <= maxd) { - ret.push_back(curE); - curE += bin; - } - return ret; -} - -// _____________________________________________________________________________ -void Collector::printCsv(std::ostream* os, const std::set& result, - const std::vector& bins) const { - auto it = result.begin(); - std::vector > res; - - for (size_t i = 0; i < bins.size(); i++) { - size_t c = 0; - const Trip* trip = 0; - - while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) { - if (!trip) trip = it->getTrip(); - c++; - it++; - } - - res.push_back(std::pair(bins[i], c)); - } - - (*os) << "range, count\n"; - for (auto r : res) { - (*os) << r.first << "," << r.second << "\n"; - } -} - -// _____________________________________________________________________________ -void Collector::printStats(std::ostream* os) const { - size_t buckets = 10; - (*os) << "\n ===== Evalution results =====\n\n"; - - (*os) << std::setfill(' ') << std::setw(30) - << " # of trips new shapes were matched for: " << _results.size() - << "\n"; - (*os) << std::setw(30) << " # of trips without input shapes: " << _noOrigShp - << "\n"; - - if (_results.size()) { - (*os) << std::setw(30) << " highest distance to input shapes: " - << (--_results.end())->getDist() << " (on trip #" - << (--_results.end())->getTrip()->getId() << ")\n"; - (*os) << std::setw(30) << " lowest distance to input shapes: " - << (_results.begin())->getDist() << " (on trip #" - << (_results.begin())->getTrip()->getId() << ")\n"; - (*os) << std::setw(30) << " avg total frechet distance: " << getAvgDist() - << "\n"; - - std::vector dfBins = getBins( - (_results.begin())->getDist(), (--_results.end())->getDist(), buckets); - - if (_dfBins.size()) dfBins = _dfBins; - - (*os) << "\n -- Histogram of d_f for this run -- " << std::endl; - printHisto(os, _results, dfBins); - - std::ofstream fstr1(_evalOutPath + "/eval-frechet.csv"); - printCsv(&fstr1, _results, dfBins); - - (*os) << "\n\n\n -- Histogram of A_N/N for this run -- " << std::endl; - printHisto(os, _resultsAN, - getBins((_resultsAN.begin())->getDist(), - (--_resultsAN.end())->getDist(), buckets)); - std::ofstream fstr2(_evalOutPath + "/eval-AN.csv"); - printCsv(&fstr2, _resultsAN, getBins(0, 1, 20)); - - (*os) << "\n\n\n -- Histogram of A_L/L for this run -- " << std::endl; - printHisto(os, _resultsAL, - getBins((_resultsAL.begin())->getDist(), - (--_resultsAL.end())->getDist(), buckets)); - std::ofstream fstr3(_evalOutPath + "/eval-AL.csv"); - printCsv(&fstr3, _resultsAL, getBins(0, 1, 20)); - } - - (*os) << "\n ===== End of evaluation results =====\n"; - (*os) << std::endl; -} - -// _____________________________________________________________________________ -std::pair Collector::getDa(const std::vector& a, - const std::vector& b) { - assert(a.size() == b.size()); - std::pair ret{0, 0}; - - // euclidean distance on web mercator is in meters on equator, - // and proportional to cos(lat) in both y directions - double fac = - cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) / - 6378137.0)) - - 1.5707965); - - for (size_t i = 0; i < a.size(); i++) { - double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac; - if (fd >= 20) { - ret.first++; - ret.second += util::geo::len(a[i]) * fac; - } - } - - return ret; -} diff --git a/src/pfaedle/gtfs/Feed.h b/src/pfaedle/gtfs/Feed.h index 8e5cc7b..dd4abdd 100644 --- a/src/pfaedle/gtfs/Feed.h +++ b/src/pfaedle/gtfs/Feed.h @@ -6,7 +6,6 @@ #define PFAEDLE_GTFS_FEED_H_ #include -#include "Route.h" #include "Service.h" #include "ShapeContainer.h" #include "StopTime.h" @@ -21,14 +20,15 @@ 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> + ad::cppgtfs::gtfs::Agency, ad::cppgtfs::gtfs::Route, + ad::cppgtfs::gtfs::Stop, Service, StopTime, Shape, ad::cppgtfs::gtfs::Fare, + ad::cppgtfs::gtfs::Container, ad::cppgtfs::gtfs::Container, + ad::cppgtfs::gtfs::NullContainer, ad::cppgtfs::gtfs::ContContainer, + ad::cppgtfs::gtfs::ContContainer, ShapeContainer, + ad::cppgtfs::gtfs::Container> Feed; typedef ad::cppgtfs::gtfs::TripB, Service, - Route, Shape> + ad::cppgtfs::gtfs::Route, Shape> Trip; } // namespace gtfs diff --git a/src/pfaedle/gtfs/Route.h b/src/pfaedle/gtfs/Route.h deleted file mode 100644 index 15ba7f0..0000000 --- a/src/pfaedle/gtfs/Route.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_GTFS_ROUTE_H_ -#define PFAEDLE_GTFS_ROUTE_H_ - -#include -#include -#include -#include -#include -#include -#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_ diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index 60f2b61..9366028 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -45,15 +45,15 @@ bool ShapeContainer::remove(const std::string& id) { // ____________________________________________________________________________ template T* ShapeContainer::get(const std::string& id) { - if (!has(id)) return 0; - return reinterpret_cast(1); + UNUSED(id); + return reinterpret_cast(0); } // ____________________________________________________________________________ template const T* ShapeContainer::get(const std::string& id) const { - if (!has(id)) return 0; - return reinterpret_cast(1); + UNUSED(id); + return reinterpret_cast(0); } // ____________________________________________________________________________ diff --git a/src/pfaedle/gtfs/StopTime.h b/src/pfaedle/gtfs/StopTime.h index fe18a52..b8a45b1 100644 --- a/src/pfaedle/gtfs/StopTime.h +++ b/src/pfaedle/gtfs/StopTime.h @@ -27,14 +27,11 @@ class StopTime { 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); + : _s(s), _sequence(seq), _dist(distTrav), _at(at), _dt(dt), _isTp(isTp) { UNUSED(hs); UNUSED(put); UNUSED(dot); UNUSED(distTrav); - UNUSED(isTp); } const typename StopT::Ref getStop() const { return _s; } @@ -42,20 +39,23 @@ class StopTime { void setShapeDistanceTravelled(double d) { _dist = d; } ad::cppgtfs::gtfs::Time getArrivalTime() const { - return ad::cppgtfs::gtfs::Time(0, 0, 0); + return _at; } ad::cppgtfs::gtfs::Time getDepartureTime() const { - return ad::cppgtfs::gtfs::Time(0, 0, 0); + return _dt; } float getShapeDistanceTravelled() const { return _dist; } uint16_t getSeq() const { return _sequence; } + bool isTp() const { return _isTp; } private: typename StopT::Ref _s; uint32_t _sequence; float _dist; + ad::cppgtfs::gtfs::Time _at, _dt; + bool _isTp; }; template diff --git a/src/pfaedle/gtfs/Writer.cpp b/src/pfaedle/gtfs/Writer.cpp index 7a0c5b3..26dd51d 100644 --- a/src/pfaedle/gtfs/Writer.cpp +++ b/src/pfaedle/gtfs/Writer.cpp @@ -13,13 +13,13 @@ #include "ad/util/CsvWriter.h" #include "pfaedle/gtfs/Writer.h" -using ad::util::CsvWriter; using ad::cppgtfs::Parser; -using pfaedle::gtfs::Writer; +using ad::util::CsvWriter; using pfaedle::getTmpFName; +using pfaedle::gtfs::Writer; // ____________________________________________________________________________ -bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { +void Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { std::ofstream fs; std::ifstream is; std::string gtfsPath(path); @@ -59,8 +59,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeCalendar(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str()); @@ -72,8 +71,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeCalendarDates(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/transfers.txt").c_str()); @@ -85,8 +83,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeTransfers(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str()); @@ -98,8 +95,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFares(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str()); @@ -111,8 +107,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFareRules(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.close(); @@ -142,14 +137,14 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFrequencies(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.close(); curFile = getTmpFName(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(); @@ -163,15 +158,12 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFeedInfo(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } - - return true; } // ____________________________________________________________________________ -bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { +void Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { auto csvw = ad::cppgtfs::Writer::getFeedInfoCsvw(os); csvw.flushLine(); csvw.writeString(f->getPublisherName()); @@ -187,12 +179,10 @@ bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { csvw.skip(); csvw.writeString(f->getVersion()); csvw.flushLine(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/agency.txt").c_str()); @@ -210,12 +200,10 @@ bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeAgency(fa, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/stops.txt").c_str()); @@ -233,35 +221,22 @@ bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { 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; +void Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const { 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); + for (auto r : sourceFeed->getRoutes()) { + w.writeRoute(r.second->getFlat(), &csvw); } - fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/calendar.txt").c_str()); @@ -279,12 +254,10 @@ bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeCalendar(c, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed, +void Writer::writeCalendarDates(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str()); @@ -303,12 +276,10 @@ bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed, w.writeCalendarDate(c, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/frequencies.txt").c_str()); @@ -326,12 +297,10 @@ bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFrequency(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/transfers.txt").c_str()); @@ -349,12 +318,10 @@ bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeTransfer(t, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str()); @@ -372,12 +339,10 @@ bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFare(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/fare_rules.txt").c_str()); @@ -395,12 +360,10 @@ bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFareRule(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/shapes.txt").c_str()); @@ -439,8 +402,6 @@ bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { } fs.close(); - - return true; } // ____________________________________________________________________________ @@ -460,7 +421,7 @@ bool Writer::writeTrips(gtfs::Feed* sourceFeed, std::ostream* os) const { } // ____________________________________________________________________________ -bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/stop_times.txt").c_str()); @@ -491,8 +452,6 @@ bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeStopTime(st, &csvw); } fs.close(); - - return true; } // ___________________________________________________________________________ @@ -508,4 +467,3 @@ void Writer::cannotWrite(const std::string& file, const std::string& file2) { ss << "(temporary file for " << file2 << ") Could not write to file"; throw ad::cppgtfs::WriterException(ss.str(), file); } - diff --git a/src/pfaedle/gtfs/Writer.h b/src/pfaedle/gtfs/Writer.h index 917195d..9b894c9 100644 --- a/src/pfaedle/gtfs/Writer.h +++ b/src/pfaedle/gtfs/Writer.h @@ -16,22 +16,22 @@ class Writer { public: Writer() {} - bool write(Feed* sourceFeed, const std::string& path) const; + void 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; + void writeFeedInfo(Feed* f, std::ostream* os) const; + void writeAgency(Feed* f, std::ostream* os) const; + void writeStops(Feed* f, std::ostream* os) const; + void writeRoutes(Feed* f, std::ostream* os) const; + void writeCalendar(Feed* f, std::ostream* os) const; + void writeCalendarDates(Feed* f, std::ostream* os) const; + void writeFrequencies(Feed* f, std::ostream* os) const; + void writeTransfers(Feed* f, std::ostream* os) const; + void writeFares(Feed* f, std::ostream* os) const; + void writeFareRules(Feed* f, std::ostream* os) const; + void writeShapes(Feed* f, std::ostream* os) const; bool writeTrips(Feed* f, std::ostream* os) const; - bool writeStopTimes(Feed* f, std::ostream* os) const; + void writeStopTimes(Feed* f, std::ostream* os) const; static void cannotWrite(const std::string& file, const std::string& file2); static void cannotWrite(const std::string& file); diff --git a/src/pfaedle/netgraph/EdgePL.h b/src/pfaedle/netgraph/EdgePL.h index 58b767f..c6803ea 100644 --- a/src/pfaedle/netgraph/EdgePL.h +++ b/src/pfaedle/netgraph/EdgePL.h @@ -26,7 +26,7 @@ namespace netgraph { class EdgePL { public: EdgePL() {} - EdgePL(const LINE& l, const std::set& trips) + EdgePL(const LINE& l, const std::vector& trips) : _l(l), _trips(trips) { for (const auto t : _trips) { _routeShortNames.insert(t->getRoute()->getShortName()); @@ -46,7 +46,7 @@ class EdgePL { private: LINE _l; - std::set _trips; + std::vector _trips; std::set _routeShortNames; std::set _tripShortNames; }; diff --git a/src/pfaedle/osm/BBoxIdx.cpp b/src/pfaedle/osm/BBoxIdx.cpp index c25d378..c841d47 100644 --- a/src/pfaedle/osm/BBoxIdx.cpp +++ b/src/pfaedle/osm/BBoxIdx.cpp @@ -31,10 +31,10 @@ bool BBoxIdx::contains(const Point& p) const { // _____________________________________________________________________________ BOX BBoxIdx::getFullWebMercBox() const { return BOX( - util::geo::latLngToWebMerc( - _root.box.getLowerLeft().getY(), _root.box.getLowerLeft().getX()), - util::geo::latLngToWebMerc( - _root.box.getUpperRight().getY(), _root.box.getUpperRight().getX())); + util::geo::latLngToWebMerc(_root.box.getLowerLeft().getY(), + _root.box.getLowerLeft().getX()), + util::geo::latLngToWebMerc(_root.box.getUpperRight().getY(), + _root.box.getUpperRight().getX())); } // _____________________________________________________________________________ diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index 3da48df..6b9e488 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -18,7 +18,6 @@ #include "pfaedle/osm/OsmBuilder.h" #include "pfaedle/osm/OsmFilter.h" #include "pfaedle/osm/Restrictor.h" -#include "pfaedle/trgraph/StatGroup.h" #include "util/Misc.h" #include "util/Nullable.h" #include "util/log/Log.h" @@ -40,20 +39,14 @@ using pfaedle::trgraph::Graph; using pfaedle::trgraph::Node; using pfaedle::trgraph::NodePL; using pfaedle::trgraph::Normalizer; -using pfaedle::trgraph::StatGroup; using pfaedle::trgraph::StatInfo; using pfaedle::trgraph::TransitEdgeLine; using util::Nullable; using util::geo::Box; -using util::geo::webMercMeterDist; +using util::geo::M_PER_DEG; // _____________________________________________________________________________ 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; } @@ -62,8 +55,8 @@ OsmBuilder::OsmBuilder() {} // _____________________________________________________________________________ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, - Graph* g, const BBoxIdx& bbox, size_t gridSize, - router::FeedStops* fs, Restrictor* res) { + Graph* g, const BBoxIdx& bbox, double gridSize, + Restrictor* res) { if (!bbox.size()) return; LOG(INFO) << "Reading OSM file " << path << " ... "; @@ -101,85 +94,93 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, // * match the filter criteria // * have been used in a way in pass 3 - LOG(VDEBUG) << "Reading bounding box nodes..."; + LOG(DEBUG) << "Reading bounding box nodes..."; skipUntil(&xml, "node"); pfxml::parser_state nodeBeg = xml.state(); pfxml::parser_state edgesBeg = readBBoxNds(&xml, &bboxNodes, &noHupNodes, filter, bbox); - LOG(VDEBUG) << "Reading relations..."; + LOG(DEBUG) << "Reading relations..."; skipUntil(&xml, "relation"); readRels(&xml, &intmRels, &nodeRels, &wayRels, filter, attrKeys[2], &rawRests); - LOG(VDEBUG) << "Reading edges..."; + LOG(DEBUG) << "Reading edges..."; xml.set_state(edgesBeg); readEdges(&xml, g, intmRels, wayRels, filter, bboxNodes, &nodes, &multNodes, noHupNodes, attrKeys[1], rawRests, res, intmRels.flat, &eTracks, opts); - LOG(VDEBUG) << "Reading kept nodes..."; + LOG(DEBUG) << "Reading kept nodes..."; xml.set_state(nodeBeg); readNodes(&xml, g, intmRels, nodeRels, filter, bboxNodes, &nodes, &multNodes, &orphanStations, attrKeys[0], intmRels.flat, opts); } - LOG(VDEBUG) << "OSM ID set lookups: " << osm::OsmIdSet::LOOKUPS - << ", file lookups: " << osm::OsmIdSet::FLOOKUPS; + LOG(DEBUG) << "OSM ID set lookups: " << osm::OsmIdSet::LOOKUPS + << ", file lookups: " << osm::OsmIdSet::FLOOKUPS; - LOG(VDEBUG) << "Applying edge track numbers..."; + LOG(DEBUG) << "Applying edge track numbers..."; writeEdgeTracks(eTracks); eTracks.clear(); { - LOG(VDEBUG) << "Fixing gaps..."; - NodeGrid ng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), false); + LOG(DEBUG) << "Fixing gaps..."; + NodeGrid ng = buildNodeIdx(g, gridSize, bbox.getFullBox(), false); + LOG(DEBUG) << "Grid size of " << ng.getXWidth() << "x" << ng.getYHeight(); fixGaps(g, &ng); } - LOG(VDEBUG) << "Writing edge geoms..."; - writeGeoms(g); + LOG(DEBUG) << "Snapping stations..."; + snapStats(opts, g, bbox, gridSize, res, orphanStations); - LOG(VDEBUG) << "Snapping stations..."; - snapStats(opts, g, bbox, gridSize, fs, res, orphanStations); - - LOG(VDEBUG) << "Deleting orphan nodes..."; - deleteOrphNds(g); - - LOG(VDEBUG) << "Deleting orphan edges..."; + LOG(DEBUG) << "Deleting orphan edges..."; deleteOrphEdgs(g, opts); - LOG(VDEBUG) << "Collapsing edges..."; + LOG(DEBUG) << "Collapsing edges..."; collapseEdges(g); - LOG(VDEBUG) << "Deleting orphan nodes..."; - deleteOrphNds(g); + LOG(DEBUG) << "Writing edge geoms..."; + writeGeoms(g, opts); - LOG(VDEBUG) << "Deleting orphan edges..."; + LOG(DEBUG) << "Deleting orphan edges..."; deleteOrphEdgs(g, opts); - LOG(VDEBUG) << "Writing graph components..."; + LOG(DEBUG) << "Deleting orphan nodes..."; + deleteOrphNds(g, opts); + + LOG(DEBUG) << "Writing graph components..."; // the restrictor is needed here to prevent connections in the graph // which are not possible in reality - uint32_t comps = writeComps(g); + uint32_t comps = writeComps(g, opts); - LOG(VDEBUG) << "Simplifying geometries..."; + LOG(DEBUG) << "Simplifying geometries..."; simplifyGeoms(g); - LOG(VDEBUG) << "Writing other-direction edges..."; + LOG(DEBUG) << "Writing other-direction edges..."; writeODirEdgs(g, res); - LOG(VDEBUG) << "Write dummy node self-edges..."; + LOG(DEBUG) << "Write wrong-direction costs..."; + writeOneWayPens(g, opts); + + if (opts.noLinesPunishFact != 1.0) { + LOG(DEBUG) << "Write no-line pens..."; + writeNoLinePens(g, opts); + } + + LOG(DEBUG) << "Write dummy node self-edges..."; writeSelfEdgs(g); size_t numEdges = 0; - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { numEdges += n->getAdjListOut().size(); } - LOG(DEBUG) << "Graph has " << g->getNds()->size() << " nodes, " << numEdges - << " edges and " << comps << " connected component(s)"; + LOG(DEBUG) << "Graph has " << g->getNds().size() << " nodes, " << numEdges + << " edges and " << comps + << " connected component(s) with more than 1 node"; + LOG(DEBUG) << _lines.size() << " transit lines have been read."; } // _____________________________________________________________________________ @@ -423,10 +424,9 @@ void OsmBuilder::readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, // _____________________________________________________________________________ NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { - NodePL ret( - util::geo::latLngToWebMerc(s->getLat(), s->getLng()), - StatInfo(ops.statNormzer.norm(s->getName()), - ops.trackNormzer.norm(s->getPlatformCode()), false)); + NodePL ret({s->getLat(), s->getLng()}, + StatInfo(ops.statNormzer.norm(s->getName()), + ops.trackNormzer.norm(s->getPlatformCode()))); #ifdef PFAEDLE_STATION_IDS // debug feature, store station id from GTFS @@ -454,8 +454,7 @@ pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, if (inNodeBlock && xml->level() == 3 && curId && strcmp(cur.name, "tag") == 0) { - if (filter.nohup(cur.attrs.find("k")->second, - cur.attrs.find("v")->second)) { + if (filter.nohup(cur.attr("k"), cur.attr("v"))) { nohupNodes->add(curId); } } @@ -466,12 +465,15 @@ pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, if (inNodeBlock) { // block ended if (strcmp(cur.name, "node")) return xml->state(); - double y = util::atof(cur.attrs.find("lat")->second, 7); - double x = util::atof(cur.attrs.find("lon")->second, 7); + double y = util::atof(cur.attr("lat"), 7); + double x = util::atof(cur.attr("lon"), 7); + + curId = util::atoul(cur.attr("id")); if (bbox.contains(Point(x, y))) { - curId = util::atoul(cur.attrs.find("id")->second); nodes->add(curId); + } else { + nodes->nadd(curId); } } } while (xml->next()); @@ -489,16 +491,16 @@ OsmWay OsmBuilder::nextWayWithId(pfxml::file* xml, osmid wid, if (xml->level() == 2 || xml->level() == 0) { if (w.id || strcmp(cur.name, "way")) return w; - osmid id = util::atoul(cur.attrs.find("id")->second); + osmid id = util::atoul(cur.attr("id")); if (id == wid) w.id = id; } if (w.id && xml->level() == 3) { if (strcmp(cur.name, "nd") == 0) { - w.nodes.push_back(util::atoul(cur.attrs.find("ref")->second)); + w.nodes.push_back(util::atoul(cur.attr("ref"))); } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - w.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + w.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -542,18 +544,18 @@ OsmWay OsmBuilder::nextWay(pfxml::file* xml, const RelMap& wayRels, if (keepWay(w, wayRels, filter, bBoxNodes, fl)) return w; if (strcmp(cur.name, "way")) return OsmWay(); - w.id = util::atoul(cur.attrs.find("id")->second); + w.id = util::atoul(cur.attr("id")); w.nodes.clear(); w.attrs.clear(); } if (w.id && xml->level() == 3) { if (strcmp(cur.name, "nd") == 0) { - osmid nid = util::atoul(cur.attrs.find("ref")->second); + osmid nid = util::atoul(cur.attr("ref")); w.nodes.push_back(nid); } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - w.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + w.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -626,6 +628,7 @@ void OsmBuilder::readEdges(pfxml::file* xml, Graph* g, const RelLst& rels, } else { n = (*nodes)[nid]; } + if (last) { auto e = g->addEdg(last, n, EdgePL()); if (!e) continue; @@ -692,14 +695,14 @@ OsmNode OsmBuilder::nextNode(pfxml::file* xml, NIdMap* nodes, if (strcmp(cur.name, "node")) return OsmNode(); n.attrs.clear(); - n.lat = util::atof(cur.attrs.find("lat")->second, 7); - n.lng = util::atof(cur.attrs.find("lon")->second, 7); - n.id = util::atoul(cur.attrs.find("id")->second); + n.lat = util::atof(cur.attr("lat"), 7); + n.lng = util::atof(cur.attr("lon"), 7); + n.id = util::atoul(cur.attr("id")); } if (xml->level() == 3 && n.id && strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - n.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + n.attrs[cur.attr("k")] = cur.attr("v"); } } while (xml->next()); @@ -755,44 +758,42 @@ void OsmBuilder::readNodes(pfxml::file* xml, Graph* g, const RelLst& rels, NIdMultMap* multNodes, NodeSet* orphanStations, const AttrKeySet& keepAttrs, const FlatRels& fl, const OsmReadOpts& opts) const { - StAttrGroups attrGroups; - OsmNode nd; while ((nd = nextNode(xml, nodes, multNodes, nodeRels, filter, bBoxNodes, keepAttrs, fl)) .id) { Node* n = 0; - auto pos = util::geo::latLngToWebMerc(nd.lat, nd.lng); + POINT pos = {nd.lng, nd.lat}; if (nodes->count(nd.id)) { n = (*nodes)[nd.id]; n->pl().setGeom(pos); if (filter.station(nd.attrs)) { - auto si = getStatInfo(n, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) n->pl().setSI(si); } else if (filter.blocker(nd.attrs)) { n->pl().setBlocker(); + } else if (filter.turnCycle(nd.attrs)) { + n->pl().setTurnCycle(); } } else if ((*multNodes).count(nd.id)) { for (auto* n : (*multNodes)[nd.id]) { n->pl().setGeom(pos); if (filter.station(nd.attrs)) { - auto si = getStatInfo(n, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) n->pl().setSI(si); } else if (filter.blocker(nd.attrs)) { n->pl().setBlocker(); + } else if (filter.turnCycle(nd.attrs)) { + n->pl().setTurnCycle(); } } } else { // these are nodes without any connected edges if (filter.station(nd.attrs)) { auto tmp = g->addNd(NodePL(pos)); - auto si = getStatInfo(tmp, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) tmp->pl().setSI(si); if (tmp->pl().getSI()) { - tmp->pl().getSI()->setIsFromOsm(false); orphanStations->insert(tmp); } } @@ -828,34 +829,34 @@ OsmRel OsmBuilder::nextRel(pfxml::file* xml, const OsmFilter& filter, rel.wayRoles.clear(); rel.keepFlags = 0; rel.dropFlags = 0; - rel.id = util::atoul(cur.attrs.find("id")->second); + rel.id = util::atoul(cur.attr("id")); } if (xml->level() == 3 && rel.id) { if (strcmp(cur.name, "member") == 0) { - if (strcmp(cur.attrs.find("type")->second, "node") == 0) { - osmid id = util::atoul(cur.attrs.find("ref")->second); + if (strcmp(cur.attr("type"), "node") == 0) { + osmid id = util::atoul(cur.attr("ref")); // TODO(patrick): no need to push IDs that have been filtered out by // the bounding box!!!! rel.nodes.push_back(id); - if (cur.attrs.count("role")) { - rel.nodeRoles.push_back(cur.attrs.find("role")->second); + if (cur.attr("role")) { + rel.nodeRoles.push_back(cur.attr("role")); } else { rel.nodeRoles.push_back(""); } } - if (strcmp(cur.attrs.find("type")->second, "way") == 0) { - osmid id = util::atoul(cur.attrs.find("ref")->second); + if (strcmp(cur.attr("type"), "way") == 0) { + osmid id = util::atoul(cur.attr("ref")); rel.ways.push_back(id); - if (cur.attrs.count("role")) { - rel.wayRoles.push_back(cur.attrs.find("role")->second); + if (cur.attr("role")) { + rel.wayRoles.push_back(cur.attr("role")); } else { rel.wayRoles.push_back(""); } } } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - rel.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + rel.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -936,14 +937,13 @@ void OsmBuilder::readRestr(const OsmRel& rel, Restrictions* rests, // _____________________________________________________________________________ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, - const AttrMap& attrs, + const AttrMap& am, const RelMap& entRels, const RelLst& rels, const Normalizer& normzer) const { std::string ret; for (const auto& s : rule) { - ret = - normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + ret = normzer.norm(pfxml::file::decode(getAttr(s, id, am, entRels, rels))); if (!ret.empty()) return ret; } @@ -952,13 +952,12 @@ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, // _____________________________________________________________________________ std::vector OsmBuilder::getAttrMatchRanked( - const DeepAttrLst& rule, osmid id, const AttrMap& attrs, - const RelMap& entRels, const RelLst& rels, - const Normalizer& normzer) const { + const DeepAttrLst& rule, osmid id, const AttrMap& am, const RelMap& entRels, + const RelLst& rels, const Normalizer& norm) const { std::vector ret; for (const auto& s : rule) { std::string tmp = - normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + norm.norm(pfxml::file::decode(getAttr(s, id, am, entRels, rels))); if (!tmp.empty()) ret.push_back(tmp); } @@ -967,11 +966,11 @@ std::vector OsmBuilder::getAttrMatchRanked( // _____________________________________________________________________________ std::string OsmBuilder::getAttr(const DeepAttrRule& s, osmid id, - const AttrMap& attrs, const RelMap& entRels, + const AttrMap& am, const RelMap& entRels, const RelLst& rels) const { if (s.relRule.kv.first.empty()) { - if (attrs.find(s.attr) != attrs.end()) { - return attrs.find(s.attr)->second; + if (am.find(s.attr) != am.end()) { + return am.find(s.attr)->second; } } else { if (entRels.count(id)) { @@ -988,9 +987,7 @@ std::string OsmBuilder::getAttr(const DeepAttrRule& s, osmid id, } // _____________________________________________________________________________ -Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, - const POINT& pos, const AttrMap& m, - StAttrGroups* groups, +Nullable OsmBuilder::getStatInfo(osmid nid, const AttrMap& m, const RelMap& nodeRels, const RelLst& rels, const OsmReadOpts& ops) const { @@ -1004,7 +1001,7 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, if (!names.size()) return Nullable(); - auto ret = StatInfo(names[0], platform, true); + auto ret = StatInfo(names[0], platform); #ifdef PFAEDLE_STATION_IDS ret.setId(getAttrByFirstMatch(ops.statAttrRules.idRule, nid, m, nodeRels, @@ -1013,62 +1010,25 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, for (size_t i = 1; i < names.size(); i++) ret.addAltName(names[i]); - bool groupFound = false; - - for (const auto& rule : ops.statGroupNAttrRules) { - if (groupFound) break; - std::string ruleVal = getAttr(rule.attr, nid, m, nodeRels, rels); - if (!ruleVal.empty()) { - // check if a matching group exists - for (auto* group : (*groups)[rule.attr.attr][ruleVal]) { - if (groupFound) break; - for (const auto* member : group->getNodes()) { - if (webMercMeterDist(*member->pl().getGeom(), pos) <= rule.maxDist) { - // ok, group is matching - groupFound = true; - if (node) group->addNode(node); - ret.setGroup(group); - break; - } - } - } - } - } - - if (!groupFound) { - for (const auto& rule : ops.statGroupNAttrRules) { - std::string ruleVal = getAttr(rule.attr, nid, m, nodeRels, rels); - if (!ruleVal.empty()) { - // add new group - StatGroup* g = new StatGroup(); - if (node) g->addNode(node); - ret.setGroup(g); - (*groups)[rule.attr.attr][ruleVal].push_back(g); - break; - } - } - } - return ret; } // _____________________________________________________________________________ double OsmBuilder::dist(const Node* a, const Node* b) { - return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); + return util::geo::haversine(*(a->pl().getGeom()), *(b->pl().getGeom())); } // _____________________________________________________________________________ -double OsmBuilder::webMercDist(const Node* a, const Node* b) { - return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); -} - -// _____________________________________________________________________________ -void OsmBuilder::writeGeoms(Graph* g) { - for (auto* n : *g->getNds()) { +void OsmBuilder::writeGeoms(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - e->pl().addPoint(*e->getFrom()->pl().getGeom()); - e->pl().setLength(dist(e->getFrom(), e->getTo())); - e->pl().addPoint(*e->getTo()->pl().getGeom()); + if (!e->pl().getGeom()) { + e->pl().addPoint(*e->getFrom()->pl().getGeom()); + e->pl().addPoint(*e->getTo()->pl().getGeom()); + } + + e->pl().setCost(costToInt(dist(e->getFrom(), e->getTo()) / + opts.levelDefSpeed[e->pl().lvl()])); } } } @@ -1076,18 +1036,18 @@ void OsmBuilder::writeGeoms(Graph* g) { // _____________________________________________________________________________ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { double METER = 1; - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { if (n->getInDeg() + n->getOutDeg() == 1) { // get all nodes in distance std::set ret; - double distor = util::geo::webMercDistFactor(*n->pl().getGeom()); + double distor = util::geo::latLngDistFactor(*n->pl().getGeom()); ng->get(util::geo::pad(util::geo::getBoundingBox(*n->pl().getGeom()), - METER / distor), + (METER / M_PER_DEG) / distor), &ret); for (auto* nb : ret) { if (nb != n && (nb->getInDeg() + nb->getOutDeg()) == 1 && - webMercDist(nb, n) <= METER / distor) { - // special case: both node are non-stations, move + dist(nb, n) <= METER) { + // special case: both nodes are non-stations, move // the end point nb to n and delete nb if (!nb->pl().getSI() && !n->pl().getSI()) { Node* otherN; @@ -1095,9 +1055,6 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { otherN = (*nb->getAdjListOut().begin())->getOtherNd(nb); else otherN = (*nb->getAdjListIn().begin())->getOtherNd(nb); - LINE l; - l.push_back(*otherN->pl().getGeom()); - l.push_back(*n->pl().getGeom()); Edge* e; if (nb->getOutDeg()) @@ -1105,7 +1062,6 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { else e = g->addEdg(otherN, n, (*nb->getAdjListIn().begin())->pl()); if (e) { - *e->pl().getGeom() = l; g->delNd(nb); ng->remove(nb); } @@ -1123,25 +1079,27 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { } // _____________________________________________________________________________ -EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size, - const BOX& webMercBox) { - EdgeGrid ret(size, size, webMercBox, false); - for (auto* n : *g->getNds()) { +EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, double size, const BOX& box) { + EdgeGrid ret(size, size, box, false); + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - assert(e->pl().getGeom()); - ret.add(*e->pl().getGeom(), e); + auto llGeom = + LINE{*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom()}; + ret.add(llGeom, e); } } return ret; } // _____________________________________________________________________________ -NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, +NodeGrid OsmBuilder::buildNodeIdx(Graph* g, double size, const BOX& box, bool which) { - NodeGrid ret(size, size, webMercBox, false); - for (auto* n : *g->getNds()) { + NodeGrid ret(size, size, box, false); + for (auto* n : g->getNds()) { + // only orphan nodes if (!which && n->getInDeg() + n->getOutDeg() == 1) ret.add(*n->pl().getGeom(), n); + // only station nodes else if (which && n->pl().getSI()) ret.add(*n->pl().getGeom(), n); } @@ -1153,8 +1111,8 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, const SearchFunc& sfunc) { // shortcuts - double dFrom = webMercMeterDist(*e->getFrom()->pl().getGeom(), p); - double dTo = webMercMeterDist(*e->getTo()->pl().getGeom(), p); + double dFrom = haversine(*e->getFrom()->pl().getGeom(), p); + double dTo = haversine(*e->getTo()->pl().getGeom(), p); if (dFrom > maxD && dTo > maxD) return 0; if (dFrom <= maxD && sfunc(e->getFrom(), si)) return e->getFrom(); @@ -1197,13 +1155,15 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, if (util::geo::innerProd(nodeP, fromP, toP) < minAngle) fullTurn = 1; } + double eLen = dist(edg->getFrom(), edg->getTo()); + if ((maxFullTurns < 0 || cur.fullTurns + fullTurn <= maxFullTurns) && - cur.dist + edg->pl().getLength() < maxD && !closed.count(cand)) { + cur.dist + eLen < maxD && !closed.count(cand)) { if (sfunc(cand, si)) { return cand; } else { - pq.push(NodeCand{cur.dist + edg->pl().getLength(), cand, edg, - cur.fullTurns + fullTurn}); + pq.push( + NodeCand{cur.dist + eLen, cand, edg, cur.fullTurns + fullTurn}); } } } @@ -1220,96 +1180,39 @@ 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, - bool orphanSnap) { - return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, - EqSearch(orphanSnap)); + double maxD, int maxFullTurns, double minAngle) { + return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, EqSearch()); } // _____________________________________________________________________________ void OsmBuilder::getEdgCands(const POINT& geom, EdgeCandPQ* ret, EdgeGrid* eg, double d) { - double distor = util::geo::webMercDistFactor(geom); + double distor = util::geo::latLngDistFactor(geom); std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(geom), d / distor); + BOX box = + util::geo::pad(util::geo::getBoundingBox(geom), (d / M_PER_DEG) / distor); eg->get(box, &neighs); for (auto* e : neighs) { double dist = util::geo::distToSegment(*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom(), geom); - if (dist * distor <= d) { + if (dist * distor * M_PER_DEG <= d) { ret->push(EdgeCand(-dist, e)); } } } // _____________________________________________________________________________ -std::set OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng, - double d) { - std::set ret; - double distor = util::geo::webMercDistFactor(*s.getGeom()); - std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); - ng->get(box, &neighs); - - for (auto* n : neighs) { - if (n->pl().getSI() && n->pl().getSI()->simi(s.getSI()) > 0.5) { - double dist = webMercMeterDist(*n->pl().getGeom(), *s.getGeom()); - if (dist < d) ret.insert(n); - } - } - - return ret; -} - -// _____________________________________________________________________________ -Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) { - double distor = util::geo::webMercDistFactor(*s.getGeom()); - std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); - ng->get(box, &neighs); - - Node* ret = 0; - double bestD = std::numeric_limits::max(); - - for (auto* n : neighs) { - if (n->pl().getSI() && n->pl().getSI()->simi(s.getSI()) > 0.5) { - double dist = webMercMeterDist(*n->pl().getGeom(), *s.getGeom()); - if (dist < d && dist < bestD) { - bestD = dist; - ret = n; - } - } - } - - return ret; -} - -// _____________________________________________________________________________ -std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, - NodeGrid* sng, const OsmReadOpts& opts, - Restrictor* restor, bool surrHeur, - bool orphSnap, double d) { +void OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, + const OsmReadOpts& opts, Restrictor* restor, + double d) { assert(s->getSI()); - std::set ret; EdgeCandPQ pq; getEdgCands(*s->getGeom(), &pq, eg, d); - if (pq.empty() && surrHeur) { - // no station found in the first round, try again with the nearest - // surrounding station with matching name - const Node* best = getMatchingNd(*s, sng, opts.maxSnapFallbackHeurDistance); - 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()) { auto* e = pq.top().second; pq.pop(); @@ -1319,96 +1222,51 @@ std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, Node* eq = 0; if (!(eq = eqStatReach(e, s->getSI(), geom, 2 * d, 0, - opts.maxAngleSnapReach, orphSnap))) { + opts.maxAngleSnapReach))) { if (e->pl().lvl() > opts.maxSnapLevel) continue; if (isBlocked(e, s->getSI(), geom, opts.maxBlockDistance, 0, opts.maxAngleSnapReach)) { continue; } - // if the projected position is near (< 2 meters) the end point of this + // if the projected position is near (< 0.5 meters) the end point of this // way and the endpoint is not already a station, place the station there. if (!e->getFrom()->pl().getSI() && - webMercMeterDist(geom, *e->getFrom()->pl().getGeom()) < 2) { + haversine(geom, *e->getFrom()->pl().getGeom()) < .5) { e->getFrom()->pl().setSI(*s->getSI()); - if (s->getSI()->getGroup()) - s->getSI()->getGroup()->addNode(e->getFrom()); - ret.insert(e->getFrom()); } else if (!e->getTo()->pl().getSI() && - webMercMeterDist(geom, *e->getTo()->pl().getGeom()) < 2) { + haversine(geom, *e->getTo()->pl().getGeom()) < .5) { e->getTo()->pl().setSI(*s->getSI()); - if (s->getSI()->getGroup()) s->getSI()->getGroup()->addNode(e->getTo()); - ret.insert(e->getTo()); } else { s->setGeom(geom); Node* n = g->addNd(*s); - if (n->pl().getSI()->getGroup()) - n->pl().getSI()->getGroup()->addNode(n); sng->add(geom, n); auto ne = g->addEdg(e->getFrom(), n, e->pl()); - ne->pl().setLength(webMercDist(n, e->getFrom())); - LINE l; - l.push_back(*e->getFrom()->pl().getGeom()); - l.push_back(*n->pl().getGeom()); - *ne->pl().getGeom() = l; - eg->add(l, ne); + ne->pl().setCost(costToInt(dist(e->getFrom(), n) / + opts.levelDefSpeed[ne->pl().lvl()])); + eg->add({*e->getFrom()->pl().getGeom(), *n->pl().getGeom()}, ne); auto nf = g->addEdg(n, e->getTo(), e->pl()); - nf->pl().setLength(webMercDist(n, e->getTo())); - LINE ll; - ll.push_back(*n->pl().getGeom()); - ll.push_back(*e->getTo()->pl().getGeom()); - *nf->pl().getGeom() = ll; - eg->add(ll, nf); + nf->pl().setCost(costToInt(dist(n, e->getTo()) / + opts.levelDefSpeed[nf->pl().lvl()])); + eg->add({*n->pl().getGeom(), *e->getTo()->pl().getGeom()}, nf); // replace edge in restrictor restor->replaceEdge(e, ne, nf); g->delEdg(e->getFrom(), e->getTo()); eg->remove(e); - ret.insert(n); } } else { // if the snapped station is very near to the original OSM station // write additional info from this snap station to the equivalent stat - if (webMercMeterDist(*s->getGeom(), *eq->pl().getGeom()) < - opts.maxOsmStationDistance) { + if (haversine(*s->getGeom(), *eq->pl().getGeom()) < 5) { if (eq->pl().getSI()->getTrack().empty()) eq->pl().getSI()->setTrack(s->getSI()->getTrack()); } - ret.insert(eq); } } - - return ret; -} - -// _____________________________________________________________________________ -StatGroup* OsmBuilder::groupStats(const NodeSet& s) { - if (!s.size()) return 0; - // reference group - StatGroup* ret = new StatGroup(); - bool used = false; - - for (auto* n : s) { - if (!n->pl().getSI()) continue; - used = true; - if (n->pl().getSI()->getGroup()) { - // this node is already in a group - merge this group with this one - ret->merge(n->pl().getSI()->getGroup()); - } else { - ret->addNode(n); - n->pl().getSI()->setGroup(ret); - } - } - - if (!used) { - delete ret; - return 0; - } - - return ret; } // _____________________________________________________________________________ @@ -1423,6 +1281,7 @@ std::vector OsmBuilder::getLines( elp = _relLines[relId]; } else { TransitEdgeLine el; + el.color = ad::cppgtfs::gtfs::NO_COLOR; bool found = false; for (const auto& r : ops.relLinerules.sNameRule) { @@ -1460,6 +1319,23 @@ std::vector OsmBuilder::getLines( if (found) break; } + found = false; + for (const auto& r : ops.relLinerules.colorRule) { + for (const auto& relAttr : rels.rels[relId]) { + if (relAttr.first == r) { + auto dec = pfxml::file::decode(relAttr.second); + auto color = parseHexColor(dec); + if (color == ad::cppgtfs::gtfs::NO_COLOR) + color = parseHexColor(std::string("#") + dec); + if (color != ad::cppgtfs::gtfs::NO_COLOR) { + found = true; + el.color = color; + } + } + } + if (found) break; + } + if (!el.shortName.size() && !el.fromStr.size() && !el.toStr.size()) continue; @@ -1481,15 +1357,6 @@ std::vector OsmBuilder::getLines( // _____________________________________________________________________________ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const { - for (const auto& i : opts.statGroupNAttrRules) { - if (i.attr.relRule.kv.first.empty()) { - sets[0].insert(i.attr.attr); - } else { - sets[2].insert(i.attr.relRule.kv.first); - sets[2].insert(i.attr.attr); - } - } - for (const auto& i : opts.keepFilter) { for (size_t j = 0; j < 3; j++) sets[j].insert(i.first); } @@ -1523,6 +1390,10 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, sets[0].insert(i.first); } + for (const auto& i : opts.turnCycleFilter) { + sets[0].insert(i.first); + } + for (uint8_t j = 0; j < 7; j++) { for (const auto& kv : *(opts.levelFilters + j)) { sets[1].insert(kv.first); @@ -1550,6 +1421,8 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, opts.relLinerules.fromNameRule.end()); sets[2].insert(opts.relLinerules.sNameRule.begin(), opts.relLinerules.sNameRule.end()); + sets[2].insert(opts.relLinerules.colorRule.begin(), + opts.relLinerules.colorRule.end()); for (const auto& i : opts.statAttrRules.nameRule) { if (i.relRule.kv.first.empty()) { @@ -1588,12 +1461,26 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, } } +// _____________________________________________________________________________ +void OsmBuilder::deleteOrphNds(Graph* g, const OsmReadOpts& opts) { + UNUSED(opts); + for (auto i = g->getNds().begin(); i != g->getNds().end();) { + if ((*i)->getInDeg() + (*i)->getOutDeg() != 0 || (*i)->pl().getSI()) { + ++i; + continue; + } + + i = g->delNd(*i); + } +} + // _____________________________________________________________________________ void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) { size_t ROUNDS = 3; for (size_t c = 0; c < ROUNDS; c++) { - for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { - if ((*i)->getInDeg() + (*i)->getOutDeg() != 1 || (*i)->pl().getSI()) { + for (auto i = g->getNds().begin(); i != g->getNds().end();) { + if ((*i)->getInDeg() + (*i)->getOutDeg() != 1 || (*i)->pl().getSI() || + (*i)->pl().isTurnCycle()) { ++i; continue; } @@ -1607,21 +1494,6 @@ void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) { } i = g->delNd(*i); - continue; - i++; - } - } -} - -// _____________________________________________________________________________ -void OsmBuilder::deleteOrphNds(Graph* g) { - for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { - if ((*i)->getInDeg() + (*i)->getOutDeg() == 0 && - !((*i)->pl().getSI() && (*i)->pl().getSI()->getGroup())) { - i = g->delNd(i); - // TODO(patrick): maybe delete from node grid? - } else { - i++; } } } @@ -1632,11 +1504,11 @@ bool OsmBuilder::edgesSim(const Edge* a, const Edge* b) { return false; if (a->pl().lvl() != b->pl().lvl()) return false; if (a->pl().getLines().size() != b->pl().getLines().size()) return false; - if (a->pl().getLines() != b->pl().getLines()) return false; if (a->pl().oneWay() && b->pl().oneWay()) { if (a->getFrom() != b->getTo() && a->getTo() != b->getFrom()) return false; } if (a->pl().isRestricted() || b->pl().isRestricted()) return false; + if (a->pl().getLines() != b->pl().getLines()) return false; return true; } @@ -1651,39 +1523,60 @@ const EdgePL& OsmBuilder::mergeEdgePL(Edge* a, Edge* b) { else n = a->getTo(); + if (a->pl().getGeom() == 0) { + a->pl().addPoint(*a->getFrom()->pl().getGeom()); + a->pl().addPoint(*a->getTo()->pl().getGeom()); + } + if (a->getTo() == n && b->getTo() == n) { // --> n <-- - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->rbegin(), - b->pl().getGeom()->rend()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->rbegin(), + b->pl().getGeom()->rend()); + } else { + a->pl().getGeom()->push_back(*b->getFrom()->pl().getGeom()); + } } else if (a->getTo() == n && b->getFrom() == n) { // --> n --> - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->begin(), - b->pl().getGeom()->end()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->begin(), + b->pl().getGeom()->end()); + } else { + a->pl().getGeom()->push_back(*b->getTo()->pl().getGeom()); + } } else if (a->getFrom() == n && b->getTo() == n) { // <-- n <-- std::reverse(a->pl().getGeom()->begin(), a->pl().getGeom()->end()); - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->rbegin(), - b->pl().getGeom()->rend()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->rbegin(), + b->pl().getGeom()->rend()); + } else { + a->pl().getGeom()->push_back(*b->getFrom()->pl().getGeom()); + } } else { // <-- n --> std::reverse(a->pl().getGeom()->begin(), a->pl().getGeom()->end()); - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->begin(), - b->pl().getGeom()->end()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->begin(), + b->pl().getGeom()->end()); + } else { + a->pl().getGeom()->push_back(*b->getTo()->pl().getGeom()); + } } - a->pl().setLength(a->pl().getLength() + b->pl().getLength()); - return a->pl(); } // _____________________________________________________________________________ void OsmBuilder::collapseEdges(Graph* g) { - for (auto* n : *g->getNds()) { - if (n->getOutDeg() + n->getInDeg() != 2 || n->pl().getSI()) continue; + for (auto n : g->getNds()) { + if (n->getOutDeg() + n->getInDeg() != 2 || n->pl().getSI() || + n->pl().isTurnCycle()) + continue; Edge* ea; Edge* eb; @@ -1699,7 +1592,7 @@ void OsmBuilder::collapseEdges(Graph* g) { } // important, we don't have a multigraph! if the same edge - // will already exist, leave this node + // already exists, leave this node if (g->getEdg(ea->getOtherNd(n), eb->getOtherNd(n))) continue; if (g->getEdg(eb->getOtherNd(n), ea->getOtherNd(n))) continue; @@ -1709,6 +1602,7 @@ void OsmBuilder::collapseEdges(Graph* g) { } else { g->addEdg(ea->getOtherNd(n), eb->getOtherNd(n), mergeEdgePL(ea, eb)); } + g->delEdg(ea->getFrom(), ea->getTo()); g->delEdg(eb->getFrom(), eb->getTo()); } @@ -1717,49 +1611,55 @@ void OsmBuilder::collapseEdges(Graph* g) { // _____________________________________________________________________________ void OsmBuilder::simplifyGeoms(Graph* g) { - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - (*e->pl().getGeom()) = util::geo::simplify(*e->pl().getGeom(), 0.5); + (*e->pl().getGeom()) = + util::geo::simplify(*e->pl().getGeom(), 0.5 / M_PER_DEG); } } } // _____________________________________________________________________________ -uint32_t OsmBuilder::writeComps(Graph* g) { - Component* comp = new Component{7}; +uint32_t OsmBuilder::writeComps(Graph* g, const OsmReadOpts& opts) { + NodePL::comps.clear(); + NodePL::comps.emplace_back(Component{0}); uint32_t numC = 0; + uint64_t numNds = 0; - for (auto* n : *g->getNds()) { - if (!n->pl().getComp()) { + double fac = opts.maxSpeedCorFac; + + for (auto* n : g->getNds()) { + if (!n->pl().getCompId()) { std::stack> q; q.push(std::pair(n, 0)); while (!q.empty()) { std::pair cur = q.top(); q.pop(); - cur.first->pl().setComp(comp); + cur.first->pl().setComp(NodePL::comps.size()); + numNds++; for (auto* e : cur.first->getAdjListOut()) { - if (e->pl().lvl() < comp->minEdgeLvl) - comp->minEdgeLvl = e->pl().lvl(); - if (!e->getOtherNd(cur.first)->pl().getComp()) + double speed = opts.levelDefSpeed[e->pl().lvl()] / fac; + if (speed > NodePL::comps.back().maxSpeed) + NodePL::comps.back().maxSpeed = speed; + if (!e->getOtherNd(cur.first)->pl().getCompId()) q.push(std::pair(e->getOtherNd(cur.first), e)); } for (auto* e : cur.first->getAdjListIn()) { - if (e->pl().lvl() < comp->minEdgeLvl) - comp->minEdgeLvl = e->pl().lvl(); - if (!e->getOtherNd(cur.first)->pl().getComp()) + double speed = opts.levelDefSpeed[e->pl().lvl()] / fac; + if (speed > NodePL::comps.back().maxSpeed) + NodePL::comps.back().maxSpeed = speed; + if (!e->getOtherNd(cur.first)->pl().getCompId()) q.push(std::pair(e->getOtherNd(cur.first), e)); } } - numC++; - comp = new Component{7}; + if (numNds > 1) numC++; + NodePL::comps.emplace_back(Component{0}); + numNds = 0; } } - // the last comp was not used - delete comp; - return numC; } @@ -1779,10 +1679,11 @@ void OsmBuilder::writeEdgeTracks(const EdgTracks& tracks) { // _____________________________________________________________________________ void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) { - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { if (g->getEdg(e->getTo(), e->getFrom())) continue; auto newE = g->addEdg(e->getTo(), e->getFrom(), e->pl().revCopy()); + assert(newE->pl().getGeom()); if (e->pl().isRestricted()) restor->duplicateEdge(e, newE); } } @@ -1790,9 +1691,45 @@ void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) { // _____________________________________________________________________________ void OsmBuilder::writeSelfEdgs(Graph* g) { - for (auto* n : *g->getNds()) { - if (n->pl().getSI() && n->getAdjListOut().size() == 0) { - g->addEdg(n, n); + // if a station only has degree 1, there is no way to arrive at this station + // without doing a full turn (because the outgoing candidate edge is always + // the incoming edge). This is a problem at end-stations. We solve this by + // adding self-edges with infinite costs - this still allows usage as + // arrivals, does not punish bends (because the node degree is still only 2) + // and prevents the usage of the edge to circumvent turn penalties + for (auto* n : g->getNds()) { + if (n->pl().getSI() && n->getAdjListOut().size() == 1) { + auto e = g->addEdg(n, n); + e->pl().setCost(std::numeric_limits::max()); + e->pl().addPoint(*e->getFrom()->pl().getGeom()); + e->pl().addPoint(*e->getTo()->pl().getGeom()); + } + } +} + +// _____________________________________________________________________________ +void OsmBuilder::writeNoLinePens(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().getLines().size() == 0) { + double c = e->pl().getCost(); + c = c / 10.0; // convert into seconds + e->pl().setCost(costToInt(c * opts.noLinesPunishFact)); + } + } + } +} + +// _____________________________________________________________________________ +void OsmBuilder::writeOneWayPens(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().oneWay() == 2) { + double c = e->pl().getCost(); + c = c / 10.0; // convert into seconds + e->pl().setCost( + costToInt(c * opts.oneWaySpeedPen + opts.oneWayEntryCost)); + } } } } @@ -1826,12 +1763,23 @@ bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) { 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(); + POINT ap, bp; - return router::angSmaller(ap, *other->pl().getGeom(), bp, ang); + if (a->pl().getGeom() && b->pl().getGeom()) { + ap = a->pl().backHop(); + bp = b->pl().backHop(); + if (a->getTo() != other) ap = a->pl().frontHop(); + if (b->getTo() != other) bp = b->pl().frontHop(); + } else { + assert(!a->pl().getGeom()); + assert(!b->pl().getGeom()); + ap = *a->getTo()->pl().getGeom(); + bp = *b->getTo()->pl().getGeom(); + if (a->getTo() != other) ap = *a->getFrom()->pl().getGeom(); + if (b->getTo() != other) bp = *b->getFrom()->pl().getGeom(); + } + + return util::geo::innerProd(*other->pl().getGeom(), ap, bp) > ang; } return false; @@ -1839,121 +1787,88 @@ bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) { // _____________________________________________________________________________ void OsmBuilder::snapStats(const OsmReadOpts& opts, Graph* g, - const BBoxIdx& bbox, size_t gridSize, - router::FeedStops* fs, Restrictor* res, - const NodeSet& orphanStations) { - NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), true); - EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullWebMercBox()); + const BBoxIdx& bbox, double gridSize, + Restrictor* res, const NodeSet& orphanStations) { + NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullBox(), true); + EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullBox()); LOG(DEBUG) << "Grid size of " << sng.getXWidth() << "x" << sng.getYHeight(); - for (double d : opts.maxSnapDistances) { + for (double d : opts.maxOsmStationDistances) { for (auto s : orphanStations) { - POINT geom = *s->pl().getGeom(); NodePL pl = s->pl(); - pl.getSI()->setIsFromOsm(false); - 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 - // station, set is-from-osm to true - if (webMercMeterDist(geom, *n->pl().getGeom()) < - opts.maxOsmStationDistance) { - if (n->pl().getSI()) n->pl().getSI()->setIsFromOsm(true); - } - } - } - } - - if (!fs) return; - - std::vector notSnapped; - - for (auto& s : *fs) { - bool snapped = false; - auto pl = plFromGtfs(s.first, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - 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(); - snapped = true; - } - } - if (!snapped) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s.first->getLat() << "," << s.first->getLng() - << ") in normal run, trying again later in orphan mode."; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } - 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 (auto& s : notSnapped) { - bool snapped = false; - auto pl = plFromGtfs(s, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - 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(); - snapped = true; - } - } - if (!snapped) { - // 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); - (*fs)[s] = dummyNode; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } else { - // only warn if it is contained in the BBOX for this graph - LOG(WARN) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - } + snapStation(g, &pl, &eg, &sng, opts, res, d); } } } + +// _____________________________________________________________________________ +uint32_t OsmBuilder::costToInt(double c) { + // always round upwards, otherwise when combined with the heuristic which + // is always rounded downwards the PQ monotonicity is not ensured anymore - + // with a downward rounding, the rounding errors may sum up so high that the + // path will get cheaper than the heuristic cost + uint32_t val = std::ceil(c * 10); + if (std::ceil(c * 10) > std::numeric_limits::max()) { + LOG(DEBUG) << "Cost " << c + << " does not fit in unsigned 32 bit integer, defaulting to " + << std::numeric_limits::max() << "."; + return std::numeric_limits::max(); + } + return val; +} + +// _____________________________________________________________________________ +uint32_t OsmBuilder::parseHexColor(std::string s) const { + // TODO(patrick): not very nice + size_t proced = 0; + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + std::string ret = " "; + if (s.size() == 7 && s[0] == '#') { + for (size_t i = 1; i < 7; i++) { + if (isdigit(s[i])) + ret[i - 1] = s[i]; + else if (isalpha(s[i]) && (s[i] > 64 && s[i] < 71)) + ret[i - 1] = s[i]; + else + return ad::cppgtfs::gtfs::NO_COLOR; + } + + return std::stoul("0x" + ret, &proced, 16); + } + + if (s.size() == 4 && s[0] == '#') { + for (size_t i = 1; i < 4; i++) { + if (isdigit(s[i])) { + ret[(i - 1) * 2] = s[i]; + ret[(i - 1) * 2 + 1] = s[i]; + } else if (isalpha(s[i]) && (s[i] > 64 && s[i] < 71)) { + ret[(i - 1) * 2] = s[i]; + ret[(i - 1) * 2 + 1] = s[i]; + } else { + return ad::cppgtfs::gtfs::NO_COLOR; + } + } + return std::stoul("0x" + ret, &proced, 16); + } + + if (s == "BLACK") return 0x00000000; + if (s == "SILVER") return 0x00C0C0C0; + if (s == "GRAY") return 0x00808080; + if (s == "WHITE") return 0x00FFFFFF; + if (s == "MAROON") return 0x00800000; + if (s == "RED") return 0x00FF0000; + if (s == "PURPLE") return 0x00800080; + if (s == "FUCHSIA") return 0x00FF00FF; + if (s == "GREEN") return 0x00008000; + if (s == "LIME") return 0x0000FF00; + if (s == "OLIVE") return 0x00808000; + if (s == "YELLOW") return 0x00FFFF00; + if (s == "NAVY") return 0x00000080; + if (s == "BLUE") return 0x000000FF; + if (s == "TEAL") return 0x00008080; + if (s == "AQUA") return 0x0000FFFF; + + if (ret.empty()) return ad::cppgtfs::gtfs::NO_COLOR; + return std::stoul("0x" + ret, &proced, 16); +} diff --git a/src/pfaedle/osm/OsmBuilder.h b/src/pfaedle/osm/OsmBuilder.h index 8b4c54f..10029c4 100644 --- a/src/pfaedle/osm/OsmBuilder.h +++ b/src/pfaedle/osm/OsmBuilder.h @@ -30,20 +30,19 @@ namespace pfaedle { namespace osm { +using ad::cppgtfs::gtfs::Stop; +using pfaedle::router::NodeSet; +using pfaedle::trgraph::Component; +using pfaedle::trgraph::Edge; using pfaedle::trgraph::EdgeGrid; -using pfaedle::trgraph::NodeGrid; -using pfaedle::trgraph::Normalizer; +using pfaedle::trgraph::EdgePL; using pfaedle::trgraph::Graph; using pfaedle::trgraph::Node; +using pfaedle::trgraph::NodeGrid; using pfaedle::trgraph::NodePL; -using pfaedle::trgraph::Edge; -using pfaedle::trgraph::EdgePL; -using pfaedle::trgraph::TransitEdgeLine; +using pfaedle::trgraph::Normalizer; using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::StatGroup; -using pfaedle::trgraph::Component; -using pfaedle::router::NodeSet; -using ad::cppgtfs::gtfs::Stop; +using pfaedle::trgraph::TransitEdgeLine; using util::Nullable; struct NodeCand { @@ -58,9 +57,8 @@ struct SearchFunc { }; struct EqSearch : public SearchFunc { - explicit EqSearch(bool orphanSnap) : orphanSnap(orphanSnap) {} + EqSearch() {} double minSimi = 0.9; - bool orphanSnap; bool operator()(const Node* cand, const StatInfo* si) const; }; @@ -87,8 +85,7 @@ class OsmBuilder { // Read the OSM file at path, and write a graph to g. Only elements // inside the bounding box will be read void read(const std::string& path, const OsmReadOpts& opts, Graph* g, - const BBoxIdx& box, size_t gridSize, router::FeedStops* fs, - Restrictor* res); + const BBoxIdx& box, double gridSize, Restrictor* res); // Based on the list of options, output an overpass XML query for getting // the data needed for routing @@ -103,8 +100,8 @@ class OsmBuilder { private: pfxml::parser_state readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, - OsmIdSet* noHupNodes, const OsmFilter& filter, - const BBoxIdx& bbox) const; + OsmIdSet* noHupNodes, const OsmFilter& filter, + const BBoxIdx& bbox) const; void readRels(pfxml::file* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels, const OsmFilter& filter, const AttrKeySet& keepAttrs, @@ -140,13 +137,14 @@ class OsmBuilder { Restrictor* restor, const FlatRels& flatRels, EdgTracks* etracks, const OsmReadOpts& opts); - void readEdges(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, - const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, - OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels); + void readEdges(pfxml::file* xml, const RelMap& wayRels, + const OsmFilter& filter, const OsmIdSet& bBoxNodes, + const AttrKeySet& keepAttrs, OsmIdList* ret, NIdMap* nodes, + const FlatRels& flatRels); - OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, - const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, - const FlatRels& flatRels) const; + OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels, + const OsmFilter& filter, const OsmIdSet& bBoxNodes, + const AttrKeySet& keepAttrs, const FlatRels& flatRels) const; bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const FlatRels& fl) const; @@ -168,52 +166,46 @@ class OsmBuilder { const AttrKeySet& keepAttrs) const; protected: - Nullable getStatInfo(Node* node, osmid nid, const POINT& pos, - const AttrMap& m, StAttrGroups* groups, + Nullable getStatInfo(osmid nid, const AttrMap& m, const RelMap& nodeRels, const RelLst& rels, const OsmReadOpts& ops) const; static void snapStats(const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox, - size_t gridSize, router::FeedStops* fs, Restrictor* res, + double gridSize, Restrictor* res, const NodeSet& orphanStations); - static void writeGeoms(Graph* g); - static void deleteOrphNds(Graph* g); + static void writeGeoms(Graph* g, const OsmReadOpts& opts); static void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts); + static void deleteOrphNds(Graph* g, const OsmReadOpts& opts); static double dist(const Node* a, const Node* b); - static double webMercDist(const Node* a, const Node* b); - static NodeGrid buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, + static NodeGrid buildNodeIdx(Graph* g, double size, const BOX& box, bool which); - static EdgeGrid buildEdgeIdx(Graph* g, size_t size, const BOX& webMercBox); + static EdgeGrid buildEdgeIdx(Graph* g, double size, const BOX& box); static void fixGaps(Graph* g, NodeGrid* ng); static void collapseEdges(Graph* g); static void writeODirEdgs(Graph* g, Restrictor* restor); static void writeSelfEdgs(Graph* g); + static void writeOneWayPens(Graph* g, const OsmReadOpts& opts); + static void writeNoLinePens(Graph* g, const OsmReadOpts& opts); static void writeEdgeTracks(const EdgTracks& tracks); static void simplifyGeoms(Graph* g); - static uint32_t writeComps(Graph* g); + static uint32_t writeComps(Graph* g, const OsmReadOpts& opts); static bool edgesSim(const Edge* a, const Edge* b); static const EdgePL& mergeEdgePL(Edge* a, Edge* b); static void getEdgCands(const POINT& s, EdgeCandPQ* ret, EdgeGrid* eg, double d); - static std::set getMatchingNds(const NodePL& s, NodeGrid* ng, - double d); - - static Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d); - - static NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, - const OsmReadOpts& opts, Restrictor* restor, - bool surHeur, bool orphSnap, double maxD); + static void snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, + const OsmReadOpts& opts, Restrictor* restor, + double maxD); // 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. static Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, - double maxD, int maxFullTurns, double maxAng, - bool orph); + double maxD, int maxFullTurns, double maxAng); static Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, @@ -223,8 +215,6 @@ class OsmBuilder { double maxD, int maxFullTurns, double minAngle); static bool keepFullTurn(const trgraph::Node* n, double ang); - static StatGroup* groupStats(const NodeSet& s); - static NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops); std::vector getLines(const std::vector& edgeRels, @@ -254,6 +244,10 @@ class OsmBuilder { bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const; + uint32_t parseHexColor(std::string) const; + + static uint32_t costToInt(double c); + std::map _lines; std::map _relLines; }; diff --git a/src/pfaedle/osm/OsmFilter.cpp b/src/pfaedle/osm/OsmFilter.cpp index 589cf5c..d180041 100644 --- a/src/pfaedle/osm/OsmFilter.cpp +++ b/src/pfaedle/osm/OsmFilter.cpp @@ -26,6 +26,7 @@ OsmFilter::OsmFilter(const OsmReadOpts& o) _posRestr(o.restrPosRestr), _negRestr(o.restrNegRestr), _noRestr(o.noRestrFilter), + _turnCycle(o.turnCycleFilter), _levels(o.levelFilters) {} // _____________________________________________________________________________ @@ -72,6 +73,11 @@ uint64_t OsmFilter::blocker(const AttrMap& attrs) const { return contained(attrs, _blocker, NODE); } +// _____________________________________________________________________________ +uint64_t OsmFilter::turnCycle(const AttrMap& attrs) const { + return contained(attrs, _turnCycle, NODE); +} + // _____________________________________________________________________________ uint64_t OsmFilter::contained(const AttrMap& attrs, const MultAttrMap& map, Type t) { diff --git a/src/pfaedle/osm/OsmFilter.h b/src/pfaedle/osm/OsmFilter.h index daf353a..05bafc7 100644 --- a/src/pfaedle/osm/OsmFilter.h +++ b/src/pfaedle/osm/OsmFilter.h @@ -27,6 +27,7 @@ class OsmFilter { uint64_t onewayrev(const AttrMap& attrs) const; uint64_t station(const AttrMap& attrs) const; uint64_t blocker(const AttrMap& attrs) const; + uint64_t turnCycle(const AttrMap& attrs) const; uint64_t negRestr(const AttrMap& attrs) const; uint64_t posRestr(const AttrMap& attrs) const; std::vector getAttrKeys() const; @@ -46,7 +47,7 @@ class OsmFilter { private: MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station, - _blocker, _posRestr, _negRestr, _noRestr; + _blocker, _posRestr, _negRestr, _noRestr, _turnCycle; const MultAttrMap* _levels; }; } // namespace osm diff --git a/src/pfaedle/osm/OsmIdSet.cpp b/src/pfaedle/osm/OsmIdSet.cpp index 8b23bf9..3f79e71 100644 --- a/src/pfaedle/osm/OsmIdSet.cpp +++ b/src/pfaedle/osm/OsmIdSet.cpp @@ -15,6 +15,7 @@ #include #include "pfaedle/Def.h" #include "pfaedle/osm/OsmIdSet.h" +#include "util/3rdparty/MurmurHash3.h" using pfaedle::osm::OsmIdSet; @@ -28,26 +29,46 @@ OsmIdSet::OsmIdSet() _last(0), _smallest(-1), _biggest(0), + _hasInv(false), _obufpos(0), _curBlock(-1), _fsize(0) { _bitset = new std::bitset(); + _bitsetNotIn = new std::bitset(); _file = openTmpFile(); _buffer = new unsigned char[BUFFER_S]; - _outBuffer = new unsigned char[OBUFFER_S]; + _outBuffer = new unsigned char[BUFFER_S]; } // _____________________________________________________________________________ OsmIdSet::~OsmIdSet() { delete _bitset; + delete _bitsetNotIn; delete[] _buffer; if (!_closed) delete[] _outBuffer; } +// _____________________________________________________________________________ +void OsmIdSet::nadd(osmid id) { + if (_closed) throw std::exception(); + + _hasInv = true; + + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + (*_bitsetNotIn)[h] = 1; + } +} + // _____________________________________________________________________________ void OsmIdSet::add(osmid id) { if (_closed) throw std::exception(); + diskAdd(id); if (_last > id) _sorted = false; @@ -55,7 +76,14 @@ void OsmIdSet::add(osmid id) { if (id < _smallest) _smallest = id; if (id > _biggest) _biggest = id; - for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1; + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + (*_bitset)[h] = 1; + } } // _____________________________________________________________________________ @@ -69,8 +97,8 @@ void OsmIdSet::diskAdd(osmid id) { _blockEnds.push_back(id); } - if (_obufpos >= OBUFFER_S) { - ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S); + if (_obufpos >= BUFFER_S) { + ssize_t w = cwrite(_file, _outBuffer, BUFFER_S); _fsize += w; _obufpos = 0; } @@ -86,7 +114,8 @@ size_t OsmIdSet::getBlock(osmid id) const { bool OsmIdSet::diskHas(osmid id) const { assert(_sorted); - if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) { + auto a = std::lower_bound(_blockEnds.begin(), _blockEnds.end(), id); + if (a != _blockEnds.end() && *a == id) { return true; } @@ -125,12 +154,23 @@ bool OsmIdSet::has(osmid id) const { LOOKUPS++; if (!_closed) close(); + // trivial cases if (id < _smallest || id > _biggest) { return false; } - for (int i = 0; i < 10; i++) { - if ((*_bitset)[hash(id, i)] == 0) return false; + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + if ((*_bitset)[h] == 0) { + return false; + } + if (_hasInv && (*_bitsetNotIn)[h] == 0) { + return true; + } } bool has = diskHas(id); @@ -249,8 +289,8 @@ size_t OsmIdSet::cread(int f, void* buf, size_t n) const { // _____________________________________________________________________________ uint32_t OsmIdSet::knuth(uint32_t in) const { - const uint32_t prime = 2654435769; - return (in * prime) >> 2; + const uint32_t a = 2654435769; + return (in * a) >> 2; } // _____________________________________________________________________________ @@ -264,11 +304,6 @@ uint32_t OsmIdSet::jenkins(uint32_t in) const { return in >> 2; } -// _____________________________________________________________________________ -uint32_t OsmIdSet::hash(uint32_t in, int i) const { - return (knuth(in) + jenkins(in) * i) % BLOOMF_BITS; -} - // _____________________________________________________________________________ int OsmIdSet::openTmpFile() const { const std::string& fname = getTmpFName("", ""); diff --git a/src/pfaedle/osm/OsmIdSet.h b/src/pfaedle/osm/OsmIdSet.h index d19f9a1..1a16b61 100644 --- a/src/pfaedle/osm/OsmIdSet.h +++ b/src/pfaedle/osm/OsmIdSet.h @@ -25,7 +25,7 @@ static const size_t BUFFER_S = 8 * 64 * 1024; static const size_t SORT_BUFFER_S = 8 * 64 * 1024; static const size_t OBUFFER_S = 8 * 1024 * 1024; -#define BLOOMF_BITS 400000000 +#define BLOOMF_BITS 214748357 /* * A disk-based set for OSM ids. Read-access for checking the presence is @@ -39,6 +39,9 @@ class OsmIdSet { // Add an OSM id void add(osmid id); + // Add an OSM id that is NOT contained + void nadd(osmid id); + // Check if an OSM id is contained bool has(osmid id) const; @@ -57,6 +60,8 @@ class OsmIdSet { osmid _smallest; osmid _biggest; + bool _hasInv; + size_t _obufpos; mutable size_t _curBlock; mutable ssize_t _curBlockSize; @@ -64,13 +69,14 @@ class OsmIdSet { // bloom filter std::bitset* _bitset; + std::bitset* _bitsetNotIn; + mutable std::vector _blockEnds; mutable size_t _fsize; uint32_t knuth(uint32_t in) const; uint32_t jenkins(uint32_t in) const; - uint32_t hash(uint32_t in, int i) const; void diskAdd(osmid id); void close() const; void sort() const; diff --git a/src/pfaedle/osm/OsmReadOpts.h b/src/pfaedle/osm/OsmReadOpts.h index 47d78ae..5678c51 100644 --- a/src/pfaedle/osm/OsmReadOpts.h +++ b/src/pfaedle/osm/OsmReadOpts.h @@ -5,14 +5,14 @@ #ifndef PFAEDLE_OSM_OSMREADOPTS_H_ #define PFAEDLE_OSM_OSMREADOPTS_H_ +#include #include -#include +#include #include #include -#include +#include #include #include -#include #include "pfaedle/osm/Osm.h" #include "pfaedle/trgraph/Graph.h" #include "pfaedle/trgraph/Normalizer.h" @@ -77,11 +77,12 @@ struct RelLineRules { AttrLst sNameRule; AttrLst fromNameRule; AttrLst toNameRule; + AttrLst colorRule; }; inline bool operator==(const RelLineRules& a, const RelLineRules& b) { return a.sNameRule == b.sNameRule && a.fromNameRule == b.fromNameRule && - a.toNameRule == b.toNameRule; + a.toNameRule == b.toNameRule && a.colorRule == b.colorRule; } struct StationAttrRules { @@ -94,21 +95,6 @@ inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) { return a.nameRule == b.nameRule && a.platformRule == b.platformRule; } -struct StatGroupNAttrRule { - DeepAttrRule attr; - double maxDist; -}; - -inline bool operator==(const StatGroupNAttrRule& a, - const StatGroupNAttrRule& b) { - return a.attr == b.attr && a.maxDist == b.maxDist; -} - -typedef std::unordered_map< - std::string, - std::unordered_map>> - StAttrGroups; - struct OsmReadOpts { OsmReadOpts() {} @@ -121,7 +107,7 @@ struct OsmReadOpts { MultAttrMap twoWayFilter; MultAttrMap stationFilter; MultAttrMap stationBlockerFilter; - std::vector statGroupNAttrRules; + MultAttrMap turnCycleFilter; trgraph::Normalizer statNormzer; trgraph::Normalizer lineNormzer; @@ -136,14 +122,23 @@ struct OsmReadOpts { uint8_t maxSnapLevel; double maxAngleSnapReach; - std::vector maxSnapDistances; - double maxSnapFallbackHeurDistance; + double maxSnapDistance; + double maxStationCandDistance; double maxBlockDistance; - double maxOsmStationDistance; + double maxSpeed; + double maxSpeedCorFac; - // TODO(patrick): this is not implemented yet - double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0}; + std::vector maxOsmStationDistances; + + // given in km/h, but store in m/s + double levelDefSpeed[8] = {85 * 0.2777, 70 * 0.2777, 55 * 0.2777, 40 * 0.2777, + 30 * 0.2777, 20 * 0.2777, 10 * 0.2777, 5 * 0.2777}; + + double oneWaySpeedPen; + double oneWayEntryCost; + + double noLinesPunishFact; double fullTurnAngle; @@ -154,9 +149,10 @@ struct OsmReadOpts { }; inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) { - if (a.maxSnapDistances.size() != b.maxSnapDistances.size()) return false; - for (size_t i = 0; i < a.maxSnapDistances.size(); i++) { - if (fabs(a.maxSnapDistances[i] - b.maxSnapDistances[i]) >= 0.1) + if (a.maxOsmStationDistances.size() != b.maxOsmStationDistances.size()) + return false; + for (size_t i = 0; i < a.maxOsmStationDistances.size(); i++) { + if (fabs(a.maxOsmStationDistances[i] - b.maxOsmStationDistances[i]) >= 0.1) return false; } @@ -173,24 +169,29 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) { a.twoWayFilter == b.twoWayFilter && a.stationFilter == b.stationFilter && a.stationBlockerFilter == b.stationBlockerFilter && - a.statGroupNAttrRules == b.statGroupNAttrRules && + a.turnCycleFilter == b.turnCycleFilter && a.statNormzer == b.statNormzer && a.lineNormzer == b.lineNormzer && a.trackNormzer == b.trackNormzer && a.relLinerules == b.relLinerules && a.statAttrRules == b.statAttrRules && a.maxSnapLevel == b.maxSnapLevel && fabs(a.maxAngleSnapReach - b.maxAngleSnapReach) < 0.1 && - fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 && - fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) < - 0.1 && + fabs(a.maxSnapDistance - b.maxSnapDistance) < 0.1 && + fabs(a.maxStationCandDistance - b.maxStationCandDistance) < 0.1 && fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 && - fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 && - fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 && - fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 && - fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 && - fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 && - fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 && - fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 && + fabs(a.levelDefSpeed[0] - b.levelDefSpeed[0]) < 0.1 && + fabs(a.levelDefSpeed[1] - b.levelDefSpeed[1]) < 0.1 && + fabs(a.levelDefSpeed[2] - b.levelDefSpeed[2]) < 0.1 && + fabs(a.levelDefSpeed[3] - b.levelDefSpeed[3]) < 0.1 && + fabs(a.levelDefSpeed[4] - b.levelDefSpeed[4]) < 0.1 && + fabs(a.levelDefSpeed[5] - b.levelDefSpeed[5]) < 0.1 && + fabs(a.levelDefSpeed[6] - b.levelDefSpeed[6]) < 0.1 && + fabs(a.levelDefSpeed[7] - b.levelDefSpeed[7]) < 0.1 && + fabs(a.oneWaySpeedPen - b.oneWaySpeedPen) < 0.1 && + fabs(a.oneWayEntryCost - b.oneWayEntryCost) < 0.1 && + fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.1 && fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 && + fabs(a.maxSpeedCorFac - b.maxSpeedCorFac) < 0.1 && + fabs(a.maxSpeed - b.maxSpeed) < 0.1 && a.restrPosRestr == b.restrPosRestr && a.restrNegRestr == b.restrNegRestr && a.noRestrFilter == b.noRestrFilter; diff --git a/src/pfaedle/router/Comp.h b/src/pfaedle/router/Comp.h index 349ff04..cdadc21 100644 --- a/src/pfaedle/router/Comp.h +++ b/src/pfaedle/router/Comp.h @@ -16,7 +16,7 @@ namespace router { using util::editDist; // _____________________________________________________________________________ -inline double statSimi(const std::string& a, const std::string& b) { +inline bool statSimi(const std::string& a, const std::string& b) { if (a == b) return 1; if (a.empty() || b.empty()) return 0; @@ -55,7 +55,7 @@ inline double statSimi(const std::string& a, const std::string& b) { } // _____________________________________________________________________________ -inline double lineSimi(const std::string& a, const std::string& b) { +inline bool lineSimi(const std::string& a, const std::string& b) { if (a == b) return 1; if (a.empty() || b.empty()) return 0; diff --git a/src/pfaedle/router/EdgePL.cpp b/src/pfaedle/router/EdgePL.cpp deleted file mode 100644 index a166b5b..0000000 --- a/src/pfaedle/router/EdgePL.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include "pfaedle/Def.h" -#include "util/geo/Geo.h" -#include "pfaedle/router/EdgePL.h" -#include "pfaedle/router/Router.h" -#include "util/String.h" - -using pfaedle::router::EdgePL; -using pfaedle::router::EdgeCost; -using pfaedle::router::EdgeList; -using pfaedle::trgraph::Node; - -// _____________________________________________________________________________ -EdgeList* EdgePL::getEdges() { return &_edges; } - -// _____________________________________________________________________________ -const EdgeList& EdgePL::getEdges() const { return _edges; } - -// _____________________________________________________________________________ -const POINT& EdgePL::frontHop() const { - if (!_edges.size()) return *_end->pl().getGeom(); - return _edges.back()->pl().frontHop(); -} - -// _____________________________________________________________________________ -const POINT& EdgePL::backHop() const { - if (!_edges.size()) return *_start->pl().getGeom(); - return _edges.front()->pl().backHop(); -} - -// _____________________________________________________________________________ -const Node* EdgePL::backNode() const { return _end; } - -// _____________________________________________________________________________ -const Node* EdgePL::frontNode() const { return _start; } - -// _____________________________________________________________________________ -const LINE* EdgePL::getGeom() const { - if (!_edges.size()) return 0; - if (!_geom.size()) { - const trgraph::Node* l = _start; - for (auto i = _edges.rbegin(); i != _edges.rend(); i++) { - const auto e = *i; - if ((e->getFrom() == l) ^ e->pl().isRev()) { - _geom.insert(_geom.end(), e->pl().getGeom()->begin(), - e->pl().getGeom()->end()); - } else { - _geom.insert(_geom.end(), e->pl().getGeom()->rbegin(), - e->pl().getGeom()->rend()); - } - l = e->getOtherNd(l); - } - } - - return &_geom; -} - -// _____________________________________________________________________________ -void EdgePL::setStartNode(const trgraph::Node* s) { _start = s; } - -// _____________________________________________________________________________ -void EdgePL::setEndNode(const trgraph::Node* e) { _end = e; } - -// _____________________________________________________________________________ -void EdgePL::setStartEdge(const trgraph::Edge* s) { _startE = s; } - -// _____________________________________________________________________________ -void EdgePL::setEndEdge(const trgraph::Edge* e) { _endE = e; } - -// _____________________________________________________________________________ -const EdgeCost& EdgePL::getCost() const { return _cost; } - -// _____________________________________________________________________________ -void EdgePL::setCost(const router::EdgeCost& c) { _cost = c; } - -// _____________________________________________________________________________ -util::json::Dict EdgePL::getAttrs() const { - util::json::Dict obj; - obj["cost"] = std::to_string(_cost.getValue()); - obj["from_edge"] = util::toString(_startE); - obj["to_edge"] = util::toString(_endE); - obj["dummy"] = _edges.size() ? "no" : "yes"; - - return obj; -} diff --git a/src/pfaedle/router/EdgePL.h b/src/pfaedle/router/EdgePL.h deleted file mode 100644 index 024b9a2..0000000 --- a/src/pfaedle/router/EdgePL.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_EDGEPL_H_ -#define PFAEDLE_ROUTER_EDGEPL_H_ - -#include -#include -#include "pfaedle/Def.h" -#include "pfaedle/router/Misc.h" -#include "util/geo/Geo.h" -#include "util/geo/GeoGraph.h" - -using util::geograph::GeoEdgePL; - -namespace pfaedle { -namespace router { - -class EdgePL { - public: - EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {} - const LINE* getGeom() const; - util::json::Dict getAttrs() const; - router::EdgeList* getEdges(); - const router::EdgeList& getEdges() const; - void setStartNode(const trgraph::Node* s); - void setEndNode(const trgraph::Node* s); - void setStartEdge(const trgraph::Edge* s); - void setEndEdge(const trgraph::Edge* s); - const router::EdgeCost& getCost() const; - void setCost(const router::EdgeCost& c); - const POINT& frontHop() const; - const POINT& backHop() const; - const trgraph::Node* frontNode() const; - const trgraph::Node* backNode() const; - - private: - router::EdgeCost _cost; - // the edges are in this field in REVERSED ORDER! - router::EdgeList _edges; - const trgraph::Node* _start; - const trgraph::Node* _end; - const trgraph::Edge* _startE; - const trgraph::Edge* _endE; - mutable LINE _geom; -}; -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_EDGEPL_H_ diff --git a/src/pfaedle/router/Graph.h b/src/pfaedle/router/Graph.h deleted file mode 100644 index 88d7345..0000000 --- a/src/pfaedle/router/Graph.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_GRAPH_H_ -#define PFAEDLE_ROUTER_GRAPH_H_ - -#include "pfaedle/trgraph/Graph.h" -#include "pfaedle/router/EdgePL.h" -#include "pfaedle/router/NodePL.h" -#include "util/graph/DirGraph.h" - -using util::geo::Point; -using util::geo::Line; - -namespace pfaedle { -namespace router { - -typedef util::graph::Edge Edge; -typedef util::graph::Node Node; -typedef util::graph::DirGraph Graph; - -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_GRAPH_H_ diff --git a/src/pfaedle/router/HopCache.cpp b/src/pfaedle/router/HopCache.cpp new file mode 100644 index 0000000..bb53290 --- /dev/null +++ b/src/pfaedle/router/HopCache.cpp @@ -0,0 +1,40 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include "pfaedle/router/HopCache.h" +#include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" + +using pfaedle::router::HopCache; +using pfaedle::trgraph::Edge; + +// _____________________________________________________________________________ +void HopCache::setMin(const Edge* a, const Edge* b, uint32_t val) { + _cache.set(a, b, val); +} + +// _____________________________________________________________________________ +void HopCache::setEx(const Edge* a, const Edge* b, uint32_t val) { + int64_t v = val; + _cache.set(a, b, -(v + 1)); +} + +// _____________________________________________________________________________ +void HopCache::setMin(const Edge* a, const std::set& b, uint32_t val) { + for (auto eb : b) _cache.set(a, eb, val); +} + +// _____________________________________________________________________________ +void HopCache::setMin(const std::set& a, const Edge* b, uint32_t val) { + for (auto ea : a) _cache.set(ea, b, val); +} + +// _____________________________________________________________________________ +std::pair HopCache::get(const Edge* a, const Edge* b) const { + int64_t v = _cache.get(a, b); + if (v < 0) return {(-v) - 1, 1}; + return {v, 0}; +} diff --git a/src/pfaedle/router/HopCache.h b/src/pfaedle/router/HopCache.h new file mode 100644 index 0000000..43d17af --- /dev/null +++ b/src/pfaedle/router/HopCache.h @@ -0,0 +1,39 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_HOPCACHE_H_ +#define PFAEDLE_ROUTER_HOPCACHE_H_ + +#include +#include +#include +#include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" + +namespace pfaedle { +namespace router { + +class HopCache { + public: + void setMin(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val); + + void setMin(const trgraph::Edge* a, const std::set& b, + uint32_t val); + + void setMin(const std::set& a, const trgraph::Edge* b, + uint32_t val); + + void setEx(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val); + + std::pair get(const trgraph::Edge* a, + const trgraph::Edge* b) const; + + private: + util::SparseMatrix _cache; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_HOPCACHE_H_ diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index 1c69c40..237c8b1 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -21,132 +21,75 @@ using ad::cppgtfs::gtfs::Stop; namespace pfaedle { namespace router { -struct NodeCand { - trgraph::Node* nd; - double pen; -}; +extern double time; struct EdgeCand { trgraph::Edge* e; double pen; + double progr; + POINT point; + int time; + std::vector depPrede; }; struct RoutingOpts { RoutingOpts() - : fullTurnPunishFac(2000), + : fullTurnPunishFac(1000), fullTurnAngle(45), - passThruStationsPunish(100), - oneWayPunishFac(1), - oneWayEdgePunish(0), - lineUnmatchedPunishFact(0.5), - noLinesPunishFact(0), + lineUnmatchedPunishFact(1), + lineNameFromUnmatchedPunishFact(1), + lineNameToUnmatchedPunishFact(1), + noLinesPunishFact(1), platformUnmatchedPen(0), stationDistPenFactor(0), + turnRestrCost(0), popReachEdge(true), noSelfHops(true) {} - double fullTurnPunishFac; + uint32_t fullTurnPunishFac; double fullTurnAngle; - double passThruStationsPunish; - double oneWayPunishFac; - double oneWayEdgePunish; double lineUnmatchedPunishFact; + double lineNameFromUnmatchedPunishFact; + double lineNameToUnmatchedPunishFact; double noLinesPunishFact; double platformUnmatchedPen; + double stationUnmatchedPen; double stationDistPenFactor; - double nonOsmPen; - double levelPunish[8]; + double nonStationPen; + uint32_t turnRestrCost; bool popReachEdge; bool noSelfHops; + bool useStations; + double transitionPen; + std::string transPenMethod; }; // _____________________________________________________________________________ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { - return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 && + return a.fullTurnPunishFac == b.fullTurnPunishFac && fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.01 && - fabs(a.passThruStationsPunish - b.passThruStationsPunish) < 0.01 && - fabs(a.oneWayPunishFac - b.oneWayPunishFac) < 0.01 && - fabs(a.oneWayEdgePunish - b.oneWayEdgePunish) < 0.01 && fabs(a.lineUnmatchedPunishFact - b.lineUnmatchedPunishFact) < 0.01 && + fabs(a.lineNameFromUnmatchedPunishFact - + b.lineNameFromUnmatchedPunishFact) < 0.01 && + fabs(a.lineNameToUnmatchedPunishFact - + b.lineNameToUnmatchedPunishFact) < 0.01 && fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.01 && fabs(a.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 && + fabs(a.stationUnmatchedPen - b.stationUnmatchedPen) < 0.01 && fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 && - fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 && - fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 && - fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 && - fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 && - fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 && - fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 && - fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 && - fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 && - fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01 && - a.popReachEdge == b.popReachEdge && a.noSelfHops == b.noSelfHops; -} - -struct EdgeCost { - 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 noLinesMeters, 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 + - noLinesMeters * o->noLinesPunishFact + - fullTurns * o->fullTurnPunishFac + - passThru * o->passThruStationsPunish + reachPen; - } - } - - float _cost; - - double getValue() const { return _cost; } -}; - -// _____________________________________________________________________________ -inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) { - return EdgeCost(a.getValue() + b.getValue()); -} - -// _____________________________________________________________________________ -inline bool operator<=(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() <= b.getValue(); -} - -// _____________________________________________________________________________ -inline bool operator==(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() == b.getValue(); -} - -// _____________________________________________________________________________ -inline bool operator>(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() > b.getValue(); -} - -// _____________________________________________________________________________ -template -inline bool angSmaller(const Point& f, const Point& m, const Point& t, - double ang) { - if (util::geo::innerProd(m, f, t) < ang) return 1; - return 0; + a.turnRestrCost == b.turnRestrCost && + fabs(a.transitionPen - b.transitionPen) < 0.01 && + fabs(a.nonStationPen - b.nonStationPen) < 0.01 && + a.transPenMethod == b.transPenMethod && + a.useStations == b.useStations && a.popReachEdge == b.popReachEdge && + a.noSelfHops == b.noSelfHops; } typedef std::set NodeSet; typedef std::set EdgeSet; typedef std::unordered_map FeedStops; -typedef std::vector NodeCandGroup; -typedef std::vector NodeCandRoute; - typedef std::vector EdgeCandGroup; +typedef std::vector EdgeCandMap; typedef std::vector EdgeCandRoute; typedef std::vector EdgeList; @@ -154,8 +97,12 @@ typedef std::vector NodeList; struct EdgeListHop { EdgeList edges; - const trgraph::Node* start; - const trgraph::Node* end; + const trgraph::Edge* start; + const trgraph::Edge* end; + double progrStart; + double progrEnd; + POINT pointStart; + POINT pointEnd; }; typedef std::vector EdgeListHops; diff --git a/src/pfaedle/router/NodePL.h b/src/pfaedle/router/NodePL.h deleted file mode 100644 index a9c2ea4..0000000 --- a/src/pfaedle/router/NodePL.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_NODEPL_H_ -#define PFAEDLE_ROUTER_NODEPL_H_ - -#include -#include -#include "pfaedle/trgraph/Graph.h" -#include "util/geo/GeoGraph.h" -#include "util/geo/Geo.h" -#include "pfaedle/Def.h" - -using util::geograph::GeoNodePL; - - -namespace pfaedle { -namespace router { - -class NodePL { - public: - NodePL() : _n(0) {} - NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT - - const POINT* getGeom() const { - return !_n ? 0 : _n->pl().getGeom(); - } - util::json::Dict getAttrs() const { - if (_n) return _n->pl().getAttrs(); - return util::json::Dict(); - } - - private: - const pfaedle::trgraph::Node* _n; -}; -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_NODEPL_H_ diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp deleted file mode 100644 index c2e0055..0000000 --- a/src/pfaedle/router/Router.cpp +++ /dev/null @@ -1,646 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifdef _OPENMP -#include -#else -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pfaedle/router/Comp.h" -#include "pfaedle/router/Router.h" -#include "pfaedle/router/RoutingAttrs.h" -#include "util/geo/output/GeoGraphJsonOutput.h" -#include "util/graph/Dijkstra.h" -#include "util/graph/EDijkstra.h" -#include "util/log/Log.h" - -using pfaedle::router::Router; -using pfaedle::router::EdgeCost; -using pfaedle::router::CostFunc; -using pfaedle::router::DistHeur; -using pfaedle::router::NCostFunc; -using pfaedle::router::NDistHeur; -using pfaedle::router::CombCostFunc; -using pfaedle::router::EdgeListHop; -using pfaedle::router::EdgeListHops; -using pfaedle::router::RoutingOpts; -using pfaedle::router::RoutingAttrs; -using pfaedle::router::HopBand; -using pfaedle::router::NodeCandRoute; -using util::graph::EDijkstra; -using util::graph::Dijkstra; -using util::geo::webMercMeterDist; - -// _____________________________________________________________________________ -EdgeCost NCostFunc::operator()(const trgraph::Node* from, - const trgraph::Edge* e, - const trgraph::Node* to) const { - UNUSED(to); - if (!from) return EdgeCost(); - - int oneway = e->pl().oneWay() == 2; - int32_t stationSkip = 0; - - return EdgeCost(e->pl().lvl() == 0 ? e->pl().getLength() : 0, - e->pl().lvl() == 1 ? e->pl().getLength() : 0, - e->pl().lvl() == 2 ? e->pl().getLength() : 0, - e->pl().lvl() == 3 ? e->pl().getLength() : 0, - e->pl().lvl() == 4 ? e->pl().getLength() : 0, - e->pl().lvl() == 5 ? e->pl().getLength() : 0, - e->pl().lvl() == 6 ? e->pl().getLength() : 0, - e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip, - e->pl().getLength() * oneway, oneway, 0, 0, 0, &_rOpts); -} - -// _____________________________________________________________________________ -EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n, - const trgraph::Edge* to) const { - if (!from) return EdgeCost(); - - uint32_t fullTurns = 0; - int oneway = from->pl().oneWay() == 2; - int32_t stationSkip = 0; - - if (n) { - if (from->getFrom() == to->getTo() && from->getTo() == to->getFrom()) { - // trivial full turn - fullTurns = 1; - } else if (n->getDeg() > 2) { - // otherwise, only intersection angles will be punished - fullTurns = router::angSmaller(from->pl().backHop(), *n->pl().getGeom(), - to->pl().frontHop(), _rOpts.fullTurnAngle); - } - - if (from->pl().isRestricted() && !_res.may(from, to, n)) oneway = 1; - - // for debugging - n->pl().setVisited(); - - if (_tgGrp && n->pl().getSI() && n->pl().getSI()->getGroup() != _tgGrp) - stationSkip = 1; - } - - double transitLinePen = transitLineCmp(from->pl()); - bool noLines = (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && - _rAttrs.fromString.empty() && from->pl().getLines().empty()); - - return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0, - from->pl().lvl() == 1 ? from->pl().getLength() : 0, - from->pl().lvl() == 2 ? from->pl().getLength() : 0, - from->pl().lvl() == 3 ? from->pl().getLength() : 0, - from->pl().lvl() == 4 ? from->pl().getLength() : 0, - from->pl().lvl() == 5 ? from->pl().getLength() : 0, - from->pl().lvl() == 6 ? from->pl().getLength() : 0, - from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns, - stationSkip, from->pl().getLength() * oneway, oneway, - from->pl().getLength() * transitLinePen, - noLines ? from->pl().getLength() : 0, 0, &_rOpts); -} - -// _____________________________________________________________________________ -double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const { - if (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && - _rAttrs.fromString.empty()) - return 0; - double best = 1; - for (const auto* l : e.getLines()) { - double cur = _rAttrs.simi(l); - - if (cur < 0.0001) return 0; - if (cur < best) best = cur; - } - - return best; -} - -// _____________________________________________________________________________ -NDistHeur::NDistHeur(const RoutingOpts& rOpts, - const std::set& tos) - : _rOpts(rOpts), _maxCentD(0) { - size_t c = 0; - double x = 0, y = 0; - for (auto to : tos) { - x += to->pl().getGeom()->getX(); - y += to->pl().getGeom()->getY(); - c++; - } - - x /= c; - y /= c; - _center = POINT(x, y); - - for (auto to : tos) { - double cur = webMercMeterDist(*to->pl().getGeom(), _center); - if (cur > _maxCentD) _maxCentD = cur; - } -} - -// _____________________________________________________________________________ -DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, - const std::set& tos) - : _rOpts(rOpts), _lvl(minLvl), _maxCentD(0) { - size_t c = 0; - double x = 0, y = 0; - for (auto to : tos) { - x += to->getFrom()->pl().getGeom()->getX(); - y += to->getFrom()->pl().getGeom()->getY(); - c++; - } - - x /= c; - y /= c; - _center = POINT(x, y); - - for (auto to : tos) { - double cur = webMercMeterDist(*to->getFrom()->pl().getGeom(), _center) * - _rOpts.levelPunish[_lvl]; - if (cur > _maxCentD) _maxCentD = cur; - } -} - -// _____________________________________________________________________________ -EdgeCost DistHeur::operator()(const trgraph::Edge* a, - const std::set& b) const { - UNUSED(b); - double cur = webMercMeterDist(*a->getFrom()->pl().getGeom(), _center) * - _rOpts.levelPunish[_lvl]; - - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -} - -// _____________________________________________________________________________ -EdgeCost NDistHeur::operator()(const trgraph::Node* a, - const std::set& b) const { - UNUSED(b); - double cur = webMercMeterDist(*a->pl().getGeom(), _center); - - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -} - -// _____________________________________________________________________________ -double CombCostFunc::operator()(const router::Edge* from, const router::Node* n, - const router::Edge* to) const { - UNUSED(n); - UNUSED(from); - return to->pl().getCost().getValue(); -} - -// _____________________________________________________________________________ -Router::Router(size_t numThreads, bool caching) - : _cache(numThreads), _caching(caching) { - for (size_t i = 0; i < numThreads; i++) { - _cache[i] = new Cache(); - } -} - -// _____________________________________________________________________________ -Router::~Router() { - for (size_t i = 0; i < _cache.size(); i++) { - delete _cache[i]; - } -} - -// _____________________________________________________________________________ -bool Router::compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const { - for (auto n1 : a) { - for (auto n2 : b) { - if (n1.e->getFrom()->pl().getComp() == n2.e->getFrom()->pl().getComp()) - return true; - } - } - - return false; -} - -// _____________________________________________________________________________ -HopBand Router::getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& 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++) { - double d = webMercMeterDist(*a[i].e->getFrom()->pl().getGeom(), - *b[j].e->getFrom()->pl().getGeom()); - if (d > pend) pend = d; - } - } - - LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters"; - - const trgraph::StatGroup* tgGrpTo = 0; - - if (b.begin()->e->getFrom()->pl().getSI()) - tgGrpTo = b.begin()->e->getFrom()->pl().getSI()->getGroup(); - - CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50); - - std::set from, to; - - for (auto e : a) from.insert(e.e); - for (auto e : b) to.insert(e.e); - - LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size() - << " edge candidates"; - - EdgeList el; - EdgeCost ret = costF.inf(); - DistHeur distH(0, rOpts, to); - - if (compConned(a, b)) - ret = EDijkstra::shortestPath(from, to, costF, distH, &el); - - if (el.size() < 2 && costF.inf() <= ret) { - LOG(VDEBUG) << "Pilot run: no connection between candidate groups," - << " setting max distance to 1"; - return HopBand{0, 1, 0, 0}; - } - - // cache the found path, will save a few dijkstra iterations - nestedCache(&el, from, costF, rAttrs); - - auto na = el.back()->getFrom(); - auto nb = el.front()->getFrom(); - - double maxStrD = 0; - - for (auto e : to) { - double d = webMercMeterDist(*el.front()->getFrom()->pl().getGeom(), - *e->getTo()->pl().getGeom()); - if (d > maxStrD) maxStrD = d; - } - - // TODO(patrick): derive the punish level here automatically - double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3 + - rOpts.fullTurnPunishFac + rOpts.platformUnmatchedPen; - double minD = ret.getValue(); - - LOG(VDEBUG) << "Pilot run: min distance between two groups is " - << ret.getValue() << " (between nodes " << na << " and " << nb - << "), using a max routing distance of " << maxD << ". The max" - << " straight line distance from the pilot target to any other " - "target node was" - << " " << maxStrD << "."; - - return HopBand{minD, maxD, el.front(), maxStrD}; -} - -// _____________________________________________________________________________ -EdgeListHops Router::routeGreedy(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - for (size_t i = 0; i < route.size() - 1; i++) { - const trgraph::StatGroup* tgGrp = 0; - std::set from, to; - for (auto c : route[i]) from.insert(c.nd); - for (auto c : route[i + 1]) to.insert(c.nd); - if (route[i + 1].begin()->nd->pl().getSI()) - tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup(); - - NCostFunc cost(rAttrs, rOpts, rest, tgGrp); - NDistHeur dist(rOpts, to); - - NodeList nodesRet; - EdgeListHop hop; - Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet); - - if (nodesRet.size() > 1) { - // careful: nodesRet is reversed! - hop.start = nodesRet.back(); - hop.end = nodesRet.front(); - } else { - // just take the first candidate if no route could be found - hop.start = *from.begin(); - hop.end = *to.begin(); - } - - ret[i] = hop; - } - - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::routeGreedy2(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - for (size_t i = 0; i < route.size() - 1; i++) { - const trgraph::StatGroup* tgGrp = 0; - std::set from, to; - - if (i == 0) - for (auto c : route[i]) from.insert(c.nd); - else - from.insert(const_cast(ret[i - 1].end)); - - for (auto c : route[i + 1]) to.insert(c.nd); - - if (route[i + 1].begin()->nd->pl().getSI()) - tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup(); - - NCostFunc cost(rAttrs, rOpts, rest, tgGrp); - NDistHeur dist(rOpts, to); - - NodeList nodesRet; - EdgeListHop hop; - Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet); - if (nodesRet.size() > 1) { - // careful: nodesRet is reversed! - hop.start = nodesRet.back(); - hop.end = nodesRet.front(); - } else { - // just take the first candidate if no route could be found - hop.start = *from.begin(); - hop.end = *to.begin(); - } - - ret[i] = hop; - } - - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const EdgeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - router::Graph cg; - return Router::route(route, rAttrs, rOpts, rest, &cg); -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const EdgeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, - router::Graph* cgraph) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - CombCostFunc ccost(rOpts); - router::Node* source = cgraph->addNd(); - router::Node* sink = cgraph->addNd(); - CombNodeMap nodes; - CombNodeMap nextNodes; - - for (size_t i = 0; i < route[0].size(); i++) { - auto e = route[0][i].e; - // we can be sure that each edge is exactly assigned to only one - // node because the transitgraph is directed - nodes[e] = cgraph->addNd(route[0][i].e->getFrom()); - cgraph->addEdg(source, nodes[e]) - ->pl() - .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - route[0][i].pen, 0)); - } - - size_t iters = EDijkstra::ITERS; - double itPerSecTot = 0; - size_t n = 0; - for (size_t i = 0; i < route.size() - 1; i++) { - nextNodes.clear(); - HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest); - - const trgraph::StatGroup* tgGrp = 0; - if (route[i + 1].begin()->e->getFrom()->pl().getSI()) - tgGrp = route[i + 1].begin()->e->getFrom()->pl().getSI()->getGroup(); - - std::set froms; - for (const auto& fr : route[i]) froms.insert(fr.e); - - for (auto eFr : froms) { - router::Node* cNodeFr = nodes.find(eFr)->second; - - EdgeSet tos; - std::map edges; - std::map pens; - std::unordered_map edgeLists; - std::unordered_map costs; - - assert(route[i + 1].size()); - - for (const auto& to : route[i + 1]) { - auto eTo = to.e; - tos.insert(eTo); - if (!nextNodes.count(eTo)) - nextNodes[eTo] = cgraph->addNd(to.e->getFrom()); - if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink); - - edges[eTo] = cgraph->addEdg(cNodeFr, nextNodes[eTo]); - pens[eTo] = to.pen; - - edgeLists[eTo] = edges[eTo]->pl().getEdges(); - edges[eTo]->pl().setStartNode(eFr->getFrom()); - // for debugging - edges[eTo]->pl().setStartEdge(eFr); - edges[eTo]->pl().setEndNode(to.e->getFrom()); - // for debugging - edges[eTo]->pl().setEndEdge(eTo); - } - - size_t iters = EDijkstra::ITERS; - auto t1 = TIME(); - - assert(tos.size()); - assert(froms.size()); - - hops(eFr, froms, tos, tgGrp, edgeLists, &costs, rAttrs, rOpts, rest, - hopBand); - double itPerSec = - (static_cast(EDijkstra::ITERS - iters)) / TOOK(t1, TIME()); - n++; - itPerSecTot += itPerSec; - - LOG(VDEBUG) << "from " << eFr << ": 1-" << tos.size() << " (" - << route[i + 1].size() << " nodes) hop took " - << EDijkstra::ITERS - iters << " iterations, " - << TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)"; - for (auto& kv : edges) { - kv.second->pl().setCost( - EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) + - costs[kv.first]); - - if (rOpts.popReachEdge && kv.second->pl().getEdges()->size()) { - if (kv.second->pl().getEdges() && - kv.second->pl().getEdges()->size()) { - // the reach edge is included, but we dont want it in the geometry - kv.second->pl().getEdges()->erase( - kv.second->pl().getEdges()->begin()); - } - } - } - } - - std::swap(nodes, nextNodes); - } - - LOG(VDEBUG) << "Hops took " << EDijkstra::ITERS - iters << " iterations," - << " average tput was " << (itPerSecTot / n) << " its/ms"; - - iters = EDijkstra::ITERS; - std::vector res; - EDijkstra::shortestPath(source, sink, ccost, &res); - size_t j = 0; - - LOG(VDEBUG) << "Optim graph solve took " << EDijkstra::ITERS - iters - << " iterations."; - - for (auto i = res.rbegin(); i != res.rend(); i++) { - const auto e = *i; - if (e->getFrom() != source && e->getTo() != sink) { - assert(e->pl().frontNode()); - assert(e->pl().backNode()); - - ret[j] = EdgeListHop{std::move(*e->pl().getEdges()), e->pl().frontNode(), - e->pl().backNode()}; - j++; - } - } - - assert(ret.size() == j); - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - router::Graph cg; - return Router::route(route, rAttrs, rOpts, rest, &cg); -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, - router::Graph* cgraph) const { - EdgeCandRoute r; - for (auto& nCands : route) { - r.emplace_back(); - for (auto n : nCands) - for (auto* e : n.nd->getAdjListOut()) - r.back().push_back(EdgeCand{e, n.pen}); - } - - return Router::route(r, rAttrs, rOpts, rest, cgraph); -} - -// _____________________________________________________________________________ -void Router::hops(trgraph::Edge* from, const std::set& froms, - const std::set tos, - const trgraph::StatGroup* tgGrp, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, HopBand hopB) const { - std::set rem; - - CostFunc cost(rAttrs, rOpts, rest, tgGrp, hopB.maxD); - - const auto& cached = getCachedHops(from, tos, edgesRet, rCosts, rAttrs); - - for (auto e : cached) { - // shortcut: if the nodes lie in two different connected components, - // the distance between them is trivially infinite - if ((rOpts.noSelfHops && (e == from || e->getFrom() == from->getFrom())) || - from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() || - e->pl().oneWay() == 2 || from->pl().oneWay() == 2) { - (*rCosts)[e] = cost.inf(); - } else { - rem.insert(e); - } - } - - LOG(VDEBUG) << "From cache: " << tos.size() - rem.size() - << ", have to cal: " << rem.size(); - - if (rem.size()) { - DistHeur dist(from->getFrom()->pl().getComp()->minEdgeLvl, rOpts, rem); - const auto& ret = EDijkstra::shortestPath(from, rem, cost, dist, edgesRet); - for (const auto& kv : ret) { - nestedCache(edgesRet.at(kv.first), froms, cost, rAttrs); - - (*rCosts)[kv.first] = kv.second; - } - } -} - -// _____________________________________________________________________________ -void Router::nestedCache(const EdgeList* el, - const std::set& froms, - const CostFunc& cost, - const RoutingAttrs& rAttrs) const { - if (!_caching) return; - if (el->size() == 0) return; - // iterate over result edges backwards - EdgeList curEdges; - EdgeCost curCost; - - size_t j = 0; - - for (auto i = el->begin(); i < el->end(); i++) { - if (curEdges.size()) { - curCost = curCost + cost(*i, (*i)->getTo(), curEdges.back()); - } - - curEdges.push_back(*i); - - if (froms.count(*i)) { - EdgeCost startC = cost(0, 0, *i) + curCost; - cache(*i, el->front(), startC, &curEdges, rAttrs); - j++; - } - } -} - -// _____________________________________________________________________________ -std::set Router::getCachedHops( - trgraph::Edge* from, const std::set& tos, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs) const { - std::set ret; - for (auto to : tos) { - 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; - } else { - ret.insert(to); - } - } - - return ret; -} - -// _____________________________________________________________________________ -void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c, - EdgeList* edges, const RoutingAttrs& rAttrs) const { - if (!_caching) return; - if (from == to) return; - (*_cache[omp_get_thread_num()])[rAttrs][from][to] = - std::pair(c, *edges); -} - -// _____________________________________________________________________________ -size_t Router::getCacheNumber() const { return _cache.size(); } diff --git a/src/pfaedle/router/Router.h b/src/pfaedle/router/Router.h index 74bf581..4ec8cb7 100644 --- a/src/pfaedle/router/Router.h +++ b/src/pfaedle/router/Router.h @@ -6,198 +6,97 @@ #define PFAEDLE_ROUTER_ROUTER_H_ #include -#include -#include #include +#include #include #include +#include #include #include #include "pfaedle/Def.h" #include "pfaedle/osm/Restrictor.h" -#include "pfaedle/router/Graph.h" +#include "pfaedle/router/HopCache.h" #include "pfaedle/router/Misc.h" #include "pfaedle/router/RoutingAttrs.h" +#include "pfaedle/router/TripTrie.h" +#include "pfaedle/router/Weights.h" #include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" #include "util/geo/Geo.h" -#include "util/graph/Dijkstra.h" #include "util/graph/EDijkstra.h" -using util::graph::EDijkstra; -using util::graph::Dijkstra; - namespace pfaedle { namespace router { -typedef std::unordered_map CombNodeMap; +constexpr static uint32_t ROUTE_INF = std::numeric_limits::max(); +constexpr static double DBL_INF = std::numeric_limits::infinity(); +constexpr static size_t NO_PREDE = std::numeric_limits::max(); + +constexpr static int MAX_ROUTE_COST_DOUBLING_STEPS = 3; + typedef std::pair HId; -typedef std::map< - RoutingAttrs, - std::unordered_map > > > - Cache; +typedef std::vector LayerCostsDAG; +typedef std::vector CostsDAG; +typedef std::vector> PredeDAG; -struct HopBand { - double minD; - double maxD; - const trgraph::Edge* nearest; - double maxInGrpDist; -}; +typedef std::unordered_map> + EdgeCostMatrix; +typedef std::unordered_map> + EdgeDistMatrix; +typedef util::graph::EDijkstra::EList TrEList; -struct CostFunc - : public EDijkstra::CostFunc { - CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& res, const trgraph::StatGroup* tgGrp, - double max) - : _rAttrs(rAttrs), - _rOpts(rOpts), - _res(res), - _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {} +typedef std::vector, uint32_t>> CostMatrix; - const RoutingAttrs& _rAttrs; - const RoutingOpts& _rOpts; - const osm::Restrictor& _res; - const trgraph::StatGroup* _tgGrp; - EdgeCost _inf; - - EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n, - const trgraph::Edge* to) const; - EdgeCost inf() const { return _inf; } - - double transitLineCmp(const trgraph::EdgePL& e) const; -}; - -struct NCostFunc - : public Dijkstra::CostFunc { - NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& res, const trgraph::StatGroup* tgGrp) - : _rAttrs(rAttrs), - _rOpts(rOpts), - _res(res), - _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - std::numeric_limits::infinity(), 0) {} - - const RoutingAttrs& _rAttrs; - const RoutingOpts& _rOpts; - const osm::Restrictor& _res; - const trgraph::StatGroup* _tgGrp; - EdgeCost _inf; - - EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e, - const trgraph::Node* to) const; - EdgeCost inf() const { return _inf; } - - double transitLineCmp(const trgraph::EdgePL& e) const; -}; - -struct DistHeur - : public EDijkstra::HeurFunc { - DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, - const std::set& tos); - - const RoutingOpts& _rOpts; - uint8_t _lvl; - POINT _center; - double _maxCentD; - EdgeCost operator()(const trgraph::Edge* a, - const std::set& b) const; -}; - -struct NDistHeur - : public Dijkstra::HeurFunc { - NDistHeur(const RoutingOpts& rOpts, const std::set& tos); - - const RoutingOpts& _rOpts; - POINT _center; - double _maxCentD; - EdgeCost operator()(const trgraph::Node* a, - const std::set& b) const; -}; - -struct CombCostFunc - : public EDijkstra::CostFunc { - explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {} - - const RoutingOpts& _rOpts; - - double operator()(const router::Edge* from, const router::Node* n, - const router::Edge* to) const; - double inf() const { return std::numeric_limits::infinity(); } +class Router { + public: + virtual ~Router() = default; + virtual std::map route(const TripTrie* trie, + const EdgeCandMap& ecm, + const RoutingOpts& rOpts, + const osm::Restrictor& rest, + HopCache* hopCache, + bool noFastHops) const = 0; }; /* * Finds the most likely route of schedule-based vehicle between stops in a * physical transportation network */ -class Router { +template +class RouterImpl : public Router { public: - // Init this router with caches for numThreads threads - explicit Router(size_t numThreads, bool caching); - ~Router(); - - // Find the most likely path through the graph for a node candidate route. - EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, const osm::Restrictor& rest, - router::Graph* cgraph) const; - - // Find the most likely path through the graph for an edge candidate route. - EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, const osm::Restrictor& rest, - router::Graph* cgraph) const; - - // Find the most likely path through cgraph for a node candidate route, but - // based on a greedy node to node approach - EdgeListHops routeGreedy(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - - // Find the most likely path through cgraph for a node candidate route, but - // based on a greedy node to node set approach - EdgeListHops routeGreedy2(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - - // Return the number of thread caches this router was initialized with - size_t getCacheNumber() const; + // Find the most likely path through the graph for a trip trie. + virtual std::map route( + const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const; private: - mutable std::vector _cache; - bool _caching; - HopBand getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; + void hops(const EdgeCandGroup& from, const EdgeCandGroup& to, + CostMatrix* rCosts, CostMatrix* dists, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, const osm::Restrictor& rest, + HopCache* hopCache, uint32_t maxCost) const; - void hops(trgraph::Edge* from, const std::set& froms, - const std::set to, const trgraph::StatGroup* tgGrp, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, HopBand hopB) const; + void hopsFast(const EdgeCandGroup& from, const EdgeCandGroup& to, + const LayerCostsDAG& initCosts, CostMatrix* rCosts, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest, - std::set getCachedHops( - trgraph::Edge* from, const std::set& to, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs) const; + HopCache* hopCache, uint32_t maxCost) const; - void cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c, - EdgeList* edges, const RoutingAttrs& rAttrs) const; + bool connected(const EdgeCand& from, const EdgeCandGroup& tos) const; + bool connected(const EdgeCandGroup& froms, const EdgeCand& to) const; - void nestedCache(const EdgeList* el, const std::set& froms, - const CostFunc& cost, const RoutingAttrs& rAttrs) const; + bool cacheDrop( - bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const; + HopCache* hopCache, const std::set& froms, + const trgraph::Edge* to, uint32_t maxCost) const; + + uint32_t addNonOverflow(uint32_t a, uint32_t b) const; }; + +#include "pfaedle/router/Router.tpp" } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/Router.tpp b/src/pfaedle/router/Router.tpp new file mode 100644 index 0000000..9b35f51 --- /dev/null +++ b/src/pfaedle/router/Router.tpp @@ -0,0 +1,614 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifdef _OPENMP +#include +#else +#define omp_get_thread_num() 0 +#define omp_get_num_procs() 1 +#endif + +#include +#include +#include +#include +#include +#include +#include + +using util::graph::EDijkstra; + +// _____________________________________________________________________________ +template +std::map RouterImpl::route( + const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const { + std::map ret; + + // the current node costs in our DAG + CostsDAG costsDAG(trie->getNds().size()); + PredeDAG predeDAG(trie->getNds().size()); + std::vector maxCosts(trie->getNds().size()); + + // skip the root node, init all to inf + for (size_t nid = 1; nid < trie->getNds().size(); nid++) { + costsDAG[nid].resize(ecm.at(nid).size(), DBL_INF); + predeDAG[nid].resize(ecm.at(nid).size(), NO_PREDE); + maxCosts.resize(ecm.at(nid).size(), 0); + } + + std::stack st; + + // init cost of all first childs + for (size_t cnid : trie->getNd(0).childs) { + st.push(cnid); + for (size_t frId = 0; frId < ecm.at(cnid).size(); frId++) { + costsDAG[cnid][frId] = ecm.at(cnid)[frId].pen; + } + } + + while (!st.empty()) { + size_t frTrNid = st.top(); + st.pop(); + const auto& frTrNd = trie->getNd(frTrNid); + for (size_t toTrNid : trie->getNd(frTrNid).childs) { + CostMatrix costM, dists; + const auto& toTrNd = trie->getNd(toTrNid); + + if (frTrNd.arr && !toTrNd.arr) { + for (size_t toId = 0; toId < costsDAG[toTrNid].size(); toId++) { + auto toCand = ecm.at(toTrNid)[toId]; + for (size_t frId : toCand.depPrede) { + double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen; + if (newC < costsDAG[toTrNid][toId]) { + costsDAG[toTrNid][toId] = newC; + predeDAG[toTrNid][toId] = frId; + } + } + } + st.push(toTrNid); + continue; + } + + const double avgDepT = frTrNd.accTime / frTrNd.trips; + const double avgArrT = toTrNd.accTime / toTrNd.trips; + + double hopDist = 0; + + if (TW::NEED_DIST) + hopDist = util::geo::haversine(frTrNd.lat, frTrNd.lng, toTrNd.lat, + toTrNd.lng); + + uint32_t newMaxCost = TW::maxCost(avgArrT - avgDepT, rOpts); + uint32_t maxCost = newMaxCost; + + bool found = false; + int step = 0; + + while (!found && step <= MAX_ROUTE_COST_DOUBLING_STEPS) { + maxCosts[toTrNid] = newMaxCost; + maxCost = newMaxCost; + + // calculate n x n hops between layers + if (noFastHops || !TW::ALLOWS_FAST_ROUTE) { + hops(ecm.at(frTrNid), ecm.at(toTrNid), &costM, &dists, toTrNd.rAttrs, + rOpts, rest, hopCache, maxCost); + } else { + hopsFast(ecm.at(frTrNid), ecm.at(toTrNid), costsDAG[frTrNid], &costM, + toTrNd.rAttrs, rOpts, rest, hopCache, maxCost); + } + + for (size_t matrixI = 0; matrixI < costM.size(); matrixI++) { + const auto& mVal = costM[matrixI]; + const size_t frId = mVal.first.first; + const size_t toId = mVal.first.second; + const uint32_t c = mVal.second; + + double mDist = 0; + + // the dists and the costM matrices have entries at exactly the same + // loc + if (TW::NEED_DIST) mDist = dists[matrixI].second; + + // calculate the transition weights + const double depT = ecm.at(frTrNid)[frId].time; + const double arrT = ecm.at(toTrNid)[toId].time; + const double w = TW::weight(c, mDist, arrT - depT, hopDist, rOpts); + + // update costs to successors in next layer + double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen + w; + if (newC < costsDAG[toTrNid][toId]) { + costsDAG[toTrNid][toId] = newC; + predeDAG[toTrNid][toId] = frId; + found = true; + } + } + + if (newMaxCost <= std::numeric_limits::max() / 2) + newMaxCost *= 2; + else + newMaxCost = std::numeric_limits::max(); + + if (newMaxCost == maxCost) break; + step++; + } + + if (!found) { + // write the cost for the NULL candidates as a fallback + for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) { + double newC = costsDAG[frTrNid][frNid] + maxCost * 100; + // in the time expanded case, there might be multiple null cands + size_t nullCId = 0; + while (nullCId < ecm.at(toTrNid).size() && + !ecm.at(toTrNid)[nullCId].e) { + if (newC < costsDAG[toTrNid][nullCId]) { + predeDAG[toTrNid][nullCId] = frNid; + costsDAG[toTrNid][nullCId] = newC; + } + nullCId++; + } + } + + // for the remaining, write dummy edges + for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) { + // skip NULL candidates + size_t toNid = 1; + while (toNid < ecm.at(toTrNid).size() && !ecm.at(toTrNid)[toNid].e) + toNid++; + for (; toNid < ecm.at(toTrNid).size(); toNid++) { + double newC = costsDAG[frTrNid][frNid] + ecm.at(toTrNid)[toNid].pen; + if (newC < costsDAG[toTrNid][toNid]) { + predeDAG[toTrNid][toNid] = frNid; + costsDAG[toTrNid][toNid] = newC; + } + } + } + } + + st.push(toTrNid); + } + } + + // update sink costs + std::unordered_map sinkCosts; + std::unordered_map frontIds; + for (auto leaf : trie->getNdTrips()) { + sinkCosts[leaf.first] = DBL_INF; + frontIds[leaf.first] = 0; + + for (size_t lastId = 0; lastId < ecm.at(leaf.first).size(); lastId++) { + double nCost = costsDAG[leaf.first][lastId]; + if (nCost < sinkCosts[leaf.first]) { + frontIds[leaf.first] = lastId; + sinkCosts[leaf.first] = nCost; + } + } + } + + // retrieve edges + for (auto leaf : trie->getNdTrips()) { + const auto leafNid = leaf.first; + auto curTrieNid = leafNid; + + while (predeDAG[curTrieNid][frontIds[leafNid]] != NO_PREDE) { + const auto curTrieParNid = trie->getNd(curTrieNid).parent; + const auto frId = predeDAG[curTrieNid][frontIds[leafNid]]; + const auto toId = frontIds[leafNid]; + + const auto frTrNd = trie->getNd(curTrieParNid); + const auto toTrNd = trie->getNd(curTrieNid); + + // skip in-node hops + if (frTrNd.arr && !toTrNd.arr) { + frontIds[leafNid] = frId; + curTrieNid = curTrieParNid; + continue; + } + + std::vector edgs; + + const auto& fr = ecm.at(curTrieParNid)[frId]; + const auto& to = ecm.at(curTrieNid)[toId]; + + // for subtracting and adding progression costs + typename TW::CostFunc costPr(toTrNd.rAttrs, rOpts, rest, ROUTE_INF); + + if (fr.e && to.e) { + // account for max progression start offset, do this exactly like + // in the hops calculation to ensure that we can find the path again + double maxProgrStart = 0; + for (const auto& fr : ecm.at(curTrieParNid)) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = costPr(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + const double maxCostRt = maxCosts[curTrieNid] + maxProgrStart; + uint32_t maxCostRtInt = maxCostRt; + + // avoid overflow + if (maxCostRt >= std::numeric_limits::max()) { + maxCostRtInt = std::numeric_limits::max(); + } + + typename TW::CostFunc cost(toTrNd.rAttrs, rOpts, rest, maxCostRtInt); + typename TW::DistHeur distH(fr.e->getFrom()->pl().getComp().maxSpeed, + rOpts, {to.e}); + + const double c = + EDijkstra::shortestPath(fr.e, to.e, cost, distH, &edgs); + // c += costPr(to.e, 0, 0) * to.progr; + + if (c < maxCostRtInt) { + // a path was found, use it + ret[leafNid].push_back( + {edgs, fr.e, to.e, fr.progr, to.progr, {}, {}}); + } else { + // no path was found, which is marked by an empty edge list + ret[leafNid].push_back({{}, fr.e, to.e, fr.progr, to.progr, {}, {}}); + } + } else { + // fallback to the position given in candidate + if (fr.e) { + ret[leafNid].push_back({edgs, fr.e, 0, fr.progr, 0, {}, to.point}); + } else if (to.e) { + ret[leafNid].push_back({edgs, 0, to.e, 0, to.progr, fr.point, {}}); + } else { + ret[leafNid].push_back({edgs, 0, 0, 0, 0, fr.point, to.point}); + } + } + frontIds[leafNid] = frId; + curTrieNid = curTrieParNid; + } + } + + return ret; +} + +// _____________________________________________________________________________ +template +void RouterImpl::hops(const EdgeCandGroup& froms, const EdgeCandGroup& tos, + CostMatrix* rCosts, CostMatrix* dists, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, + uint32_t maxCost) const { + // standard 1 -> n approach + std::set eFrs; + for (const auto& from : froms) { + if (!from.e) continue; + eFrs.insert(from.e); + } + + std::set eTos; + for (const auto& to : tos) { + if (!to.e) continue; + eTos.insert(to.e); + } + + EdgeCostMatrix ecm; + EdgeDistMatrix ecmDist; + + // account for max progression start offset + double maxProgrStart = 0; + typename TW::CostFunc cost(rAttrs, rOpts, rest, ROUTE_INF); + for (const auto& fr : froms) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = cost(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + maxCost = addNonOverflow(maxCost, maxProgrStart); + typename TW::CostFunc costF(rAttrs, rOpts, rest, maxCost); + + for (trgraph::Edge* eFrom : eFrs) { + std::set remTos; + for (trgraph::Edge* eTo : eTos) { + // init ecmDist + ecmDist[eFrom][eTo] = ROUTE_INF; + + std::pair cached = {0, 0}; + if (hopCache) cached = hopCache->get(eFrom, eTo); + + // shortcut: if the nodes lie in two different connected components, + // the distance between them is trivially infinite + if (eFrom->getFrom()->pl().getCompId() != + eTo->getTo()->pl().getCompId()) { + ecm[eFrom][eTo] = costF.inf(); + } else if (cached.second >= costF.inf()) { + ecm[eFrom][eTo] = costF.inf(); + } else if (!TW::NEED_DIST && cached.second) { + ecm[eFrom][eTo] = cached.first; + } else { + remTos.insert(eTo); + } + } + + if (remTos.size()) { + typename TW::DistHeur distH(eFrom->getFrom()->pl().getComp().maxSpeed, + rOpts, remTos); + + std::unordered_map paths; + std::unordered_map pathPtrs; + for (auto to : tos) pathPtrs[to.e] = &paths[to.e]; + + const auto& costs = + EDijkstra::shortestPath(eFrom, remTos, costF, distH, pathPtrs); + + for (const auto& c : costs) { + ecm[eFrom][c.first] = c.second; + + if (paths[c.first].size() == 0) { + if (hopCache) hopCache->setMin(eFrom, c.first, maxCost); + continue; // no path found + } + + if (hopCache) hopCache->setEx(eFrom, c.first, c.second); + } + + if (TW::NEED_DIST) { + for (const auto& c : costs) { + if (!paths[c.first].size()) continue; + double d = 0; + // don't count last edge + for (size_t i = paths[c.first].size() - 1; i > 0; i--) { + d += paths[c.first][i]->pl().getLength(); + } + ecmDist[eFrom][c.first] = d; + } + } + } + } + + // build return costs + for (size_t frId = 0; frId < froms.size(); frId++) { + auto fr = froms[frId]; + if (!fr.e) continue; + auto costFr = costF(fr.e, 0, 0); + for (size_t toId = 0; toId < tos.size(); toId++) { + auto to = tos[toId]; + if (!to.e) continue; + auto costTo = costF(to.e, 0, 0); + + uint32_t c = ecm[fr.e][to.e]; + + if (c >= maxCost) continue; + + double dist = 0; + if (TW::NEED_DIST) dist = ecmDist[fr.e][to.e]; + + if (fr.e == to.e) { + if (fr.progr <= to.progr) { + const uint32_t progrCFr = costFr * fr.progr; + const uint32_t progrCTo = costTo * to.progr; + + // calculate this in one step to avoid uint32_t underflow below + c += progrCTo - progrCFr; + } else { + // trivial case we can ignore + continue; + } + + } else { + // subtract progression cost on first edge + if (fr.progr > 0) { + const uint32_t progrCFr = costFr * fr.progr; + c -= progrCFr; + if (TW::NEED_DIST) dist -= fr.e->pl().getLength() * fr.progr; + } + + // add progression cost on last edge + if (to.progr > 0) { + const uint32_t progrCTo = costTo * to.progr; + c += progrCTo; + if (TW::NEED_DIST) dist += to.e->pl().getLength() * to.progr; + } + } + + if (c < maxCost) { + rCosts->push_back({{frId, toId}, c}); + if (TW::NEED_DIST) dists->push_back({{frId, toId}, dist}); + } + } + } +} + +// _____________________________________________________________________________ +template +void RouterImpl::hopsFast(const EdgeCandGroup& froms, + const EdgeCandGroup& tos, + const LayerCostsDAG& rawInitCosts, + CostMatrix* rCosts, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, + const osm::Restrictor& restr, HopCache* hopCache, + uint32_t maxCost) const { + std::unordered_map initCosts; + + std::set eFrs, eTos; + std::map> eFrCands, eToCands; + double maxSpeed = 0; + for (size_t frId = 0; frId < froms.size(); frId++) { + if (rawInitCosts[frId] >= DBL_INF || !connected(froms[frId], tos)) continue; + + eFrs.insert(froms[frId].e); + eFrCands[froms[frId].e].push_back(frId); + + if (froms[frId].e->getFrom()->pl().getComp().maxSpeed > maxSpeed) + maxSpeed = froms[frId].e->getFrom()->pl().getComp().maxSpeed; + } + + for (size_t toId = 0; toId < tos.size(); toId++) { + if (!connected(froms, tos[toId])) + continue; // skip nodes not conn'ed to any + + if (hopCache && cacheDrop(hopCache, eFrs, tos[toId].e, maxCost)) + continue; // skip nodes we have already encountered at higher cost + + eTos.insert(tos[toId].e); + eToCands[tos[toId].e].push_back(toId); + } + + if (eFrs.size() == 0 || eTos.size() == 0) return; + + // account for max progression start offset + double maxProgrStart = 0; + typename TW::CostFunc progrCostF(rAttrs, rOpts, restr, ROUTE_INF); + for (const auto& fr : froms) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = progrCostF(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + // initialize init doubles + LayerCostsDAG prepInitCosts(froms.size()); + for (size_t frId = 0; frId < froms.size(); frId++) { + if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue; + const auto& fr = froms[frId]; + // offset by progr start + double progrStart = progrCostF(fr.e, 0, 0) * fr.progr; + prepInitCosts[frId] = + TW::invWeight(rawInitCosts[frId], rOpts) + maxProgrStart - progrStart; + } + + // all init costs are inf + for (const auto& fr : froms) initCosts[fr.e] = ROUTE_INF; + + // now chose the best offset cost + for (size_t frId = 0; frId < froms.size(); frId++) { + if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue; + const auto& fr = froms[frId]; + if (prepInitCosts[frId] < initCosts[fr.e]) + initCosts[fr.e] = prepInitCosts[frId]; + } + + // get max init costs + uint32_t maxInit = 0; + uint32_t minInit = ROUTE_INF; + for (const auto& c : initCosts) { + if (!eFrs.count(c.first)) continue; + if (c.second != ROUTE_INF && c.second > maxInit) maxInit = c.second; + if (c.second < minInit) minInit = c.second; + } + + for (auto& c : initCosts) c.second = c.second - minInit; + + // account for start offsets + maxCost = addNonOverflow(maxCost, maxProgrStart); + + typename TW::CostFunc costF(rAttrs, rOpts, restr, + maxCost + (maxInit - minInit)); + + std::unordered_map paths; + std::unordered_map pathPtrs; + for (const auto& to : tos) pathPtrs[to.e] = &paths[to.e]; + + typename TW::DistHeur distH(maxSpeed, rOpts, eTos); + + const auto& costs = + EDijkstra::shortestPath(eFrs, eTos, initCosts, maxCost, costF, distH); + + for (const auto& c : costs) { + auto toEdg = c.first; + if (c.second.second >= costF.inf()) { + if (hopCache) hopCache->setMin(eFrs, toEdg, maxCost); + continue; // no path found + } + auto fromEdg = c.second.first; + uint32_t cost = c.second.second - initCosts[fromEdg]; + + if (cost >= maxCost) continue; + + for (size_t frId : eFrCands.find(fromEdg)->second) { + const auto& fr = froms[frId]; + auto costFr = costF(fr.e, 0, 0); + + for (size_t toId : eToCands.find(toEdg)->second) { + const auto& to = tos[toId]; + uint32_t wrCost = cost; + + if (fr.e == to.e) { + if (fr.progr <= to.progr) { + const auto costTo = costF(to.e, 0, 0); + const uint32_t progrCFr = costFr * fr.progr; + const uint32_t progrCTo = costTo * to.progr; + + // calculate this in one step to avoid uint32_t underflow below + wrCost += progrCTo - progrCFr; + } else { + // trivial case we can ignore + continue; + } + } else { + // subtract progression cost on first edge + if (fr.progr > 0) { + const uint32_t progrCFr = costFr * fr.progr; + wrCost -= progrCFr; + } + + // add progression cost on last edge + if (to.progr > 0) { + const auto costTo = costF(to.e, 0, 0); + const uint32_t progrCTo = costTo * to.progr; + wrCost += progrCTo; + } + } + + if (wrCost >= maxCost - maxProgrStart) continue; + + rCosts->push_back({{frId, toId}, wrCost}); + } + } + } +} + +// _____________________________________________________________________________ +template +bool RouterImpl::connected(const EdgeCand& fr, + const EdgeCandGroup& tos) const { + if (!fr.e) return false; + for (const auto& to : tos) { + if (!to.e) continue; + if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId()) + return true; + } + return false; +} + +// _____________________________________________________________________________ +template +bool RouterImpl::connected(const EdgeCandGroup& froms, + const EdgeCand& to) const { + if (!to.e) return false; + for (const auto& fr : froms) { + if (!fr.e) continue; + if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId()) + return true; + } + return false; +} + +// _____________________________________________________________________________ +template +bool RouterImpl::cacheDrop(HopCache* hopCache, + const std::set& froms, + const trgraph::Edge* to, + uint32_t maxCost) const { + for (auto fr : froms) + if (hopCache->get(fr, to).first <= maxCost) return false; + + return true; +} + +// _____________________________________________________________________________ +template +uint32_t RouterImpl::addNonOverflow(uint32_t a, uint32_t b) const { + if (a == std::numeric_limits::max() || + b == std::numeric_limits::max()) + return std::numeric_limits::max(); + uint32_t res = a + b; + if (res >= a && res >= b) return res; + return std::numeric_limits::max(); +} diff --git a/src/pfaedle/router/RoutingAttrs.h b/src/pfaedle/router/RoutingAttrs.h index 11a5cdb..c1b001f 100644 --- a/src/pfaedle/router/RoutingAttrs.h +++ b/src/pfaedle/router/RoutingAttrs.h @@ -5,8 +5,10 @@ #ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_ #define PFAEDLE_ROUTER_ROUTINGATTRS_H_ -#include +#include +#include #include +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/EdgePL.h" using pfaedle::trgraph::TransitEdgeLine; @@ -14,40 +16,74 @@ using pfaedle::trgraph::TransitEdgeLine; namespace pfaedle { namespace router { +struct LineSimilarity { + bool nameSimilar : 1; + bool fromSimilar : 1; + bool toSimilar : 1; +}; + +inline bool operator<(const LineSimilarity& a, const LineSimilarity& b) { + return (a.nameSimilar + a.fromSimilar + a.toSimilar) < + (b.nameSimilar + b.fromSimilar + b.toSimilar); +} + struct RoutingAttrs { - RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {} - std::string fromString; - std::string toString; + RoutingAttrs() + : lineFrom(""), lineTo(), shortName(""), classifier(0), _simiCache() {} + std::string lineFrom; + std::vector lineTo; std::string shortName; - mutable std::map _simiCache; + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier; + + mutable std::unordered_map _simiCache; + + LineSimilarity simi(const TransitEdgeLine* line) const { + // shortcut, if we don't have a line information, classify as similar + if (line->shortName.empty() && line->toStr.empty() && line->fromStr.empty()) + return {true, true, true}; - // carfull: lower return value = higher similarity - double simi(const TransitEdgeLine* line) const { auto i = _simiCache.find(line); if (i != _simiCache.end()) return i->second; - double cur = 1; + LineSimilarity ret{false, false, false}; + if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5) - cur -= 0.333333333; + ret.nameSimilar = true; - if (toString.empty() || line->toStr.empty() || - router::statSimi(line->toStr, toString) > 0.5) - cur -= 0.333333333; + if (lineTo.size() == 0) { + ret.toSimilar = true; + } else { + for (const auto& lTo : lineTo) { + if (lTo.empty() || classifier->similar(line->toStr, lTo)) { + ret.toSimilar = true; + break; + } + } + } - if (fromString.empty() || line->fromStr.empty() || - router::statSimi(line->fromStr, fromString) > 0.5) - cur -= 0.333333333; + if (lineFrom.empty() || classifier->similar(line->fromStr, lineFrom)) + ret.fromSimilar = true; - _simiCache[line] = cur; + _simiCache[line] = ret; - return cur; + return ret; + } + + void merge(const RoutingAttrs& other) { + assert(other.lineFrom == lineFrom); + assert(other.shortName == shortName); + + for (const auto& l : other.lineTo) { + auto i = std::lower_bound(lineTo.begin(), lineTo.end(), l); + if (i != lineTo.end() && (*i) == l) continue; // already present + lineTo.insert(i, l); + } } }; inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) { - return a.shortName == b.shortName && a.toString == b.toString && - a.fromString == b.fromString; + return a.shortName == b.shortName && a.lineFrom == b.lineFrom; } inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) { @@ -55,10 +91,8 @@ inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) { } inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) { - return a.fromString < b.fromString || - (a.fromString == b.fromString && a.toString < b.toString) || - (a.fromString == b.fromString && a.toString == b.toString && - a.shortName < b.shortName); + return a.lineFrom < b.lineFrom || + (a.lineFrom == b.lineFrom && a.shortName < b.shortName); } } // namespace router diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index cd5c85b..6223cfe 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -2,27 +2,23 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi -#ifdef _OPENMP -#include -#else -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#endif - -#include +#include +#include +#include #include #include +#include #include +#include #include #include #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" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "util/geo/Geo.h" #include "util/geo/output/GeoGraphJsonOutput.h" #include "util/geo/output/GeoJsonOutput.h" @@ -33,255 +29,515 @@ using util::geo::DBox; using util::geo::DPoint; using util::geo::extendBox; using util::geo::minbox; +using util::geo::PolyLine; +using ad::cppgtfs::gtfs::NO_COLOR; using ad::cppgtfs::gtfs::ShapePoint; using ad::cppgtfs::gtfs::Stop; using pfaedle::gtfs::Feed; using pfaedle::gtfs::StopTime; using pfaedle::gtfs::Trip; using pfaedle::osm::BBoxIdx; -using pfaedle::router::Clusters; +using pfaedle::router::EdgeCandGroup; +using pfaedle::router::EdgeCandMap; using pfaedle::router::EdgeListHops; using pfaedle::router::FeedStops; -using pfaedle::router::NodeCandGroup; -using pfaedle::router::NodeCandRoute; using pfaedle::router::RoutingAttrs; using pfaedle::router::ShapeBuilder; +using pfaedle::router::Stats; +using pfaedle::router::TripForests; +using pfaedle::router::TripTrie; +using pfaedle::statsimiclassifier::JaccardClassifier; +using pfaedle::trgraph::EdgeGrid; +using pfaedle::trgraph::NodeGrid; using util::geo::latLngToWebMerc; -using util::geo::webMercMeterDist; -using util::geo::webMercToLatLng; +using util::geo::M_PER_DEG; using util::geo::output::GeoGraphJsonOutput; // _____________________________________________________________________________ -ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, - MOTs mots, const config::MotConfig& motCfg, - eval::Collector* ecoll, pfaedle::trgraph::Graph* g, - router::FeedStops* fStops, osm::Restrictor* restr, - const config::Config& cfg) +ShapeBuilder::ShapeBuilder( + Feed* feed, MOTs mots, const config::MotConfig& motCfg, + pfaedle::trgraph::Graph* g, router::FeedStops* fStops, + osm::Restrictor* restr, + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier, + router::Router* router, const config::Config& cfg) : _feed(feed), - _evalFeed(evalFeed), _mots(mots), _motCfg(motCfg), - _ecoll(ecoll), _cfg(cfg), _g(g), - _crouter(omp_get_num_procs(), cfg.useCaching), _stops(fStops), _curShpCnt(0), - _restr(restr) { - _numThreads = _crouter.getCacheNumber(); + _restr(restr), + _classifier(classifier), + _router(router) { + pfaedle::osm::BBoxIdx box(BOX_PADDING); + ShapeBuilder::getGtfsBox(feed, mots, cfg.shapeTripId, cfg.dropShapes, &box, + _motCfg.osmBuildOpts.maxSpeed); + + _eGrid = EdgeGrid(cfg.gridSize, cfg.gridSize, box.getFullBox(), false); + _nGrid = NodeGrid(cfg.gridSize, cfg.gridSize, box.getFullBox(), false); + + LOG(DEBUG) << "Grid size of " << _nGrid.getXWidth() << "x" + << _nGrid.getYHeight(); + + buildIndex(); } // _____________________________________________________________________________ -const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const { - if (_stops->find(s) == _stops->end() || _stops->at(s) == 0) return _emptyNCG; - return _stops->at(s)->pl().getSI()->getGroup()->getNodeCands(s); +void ShapeBuilder::buildIndex() { + for (auto* n : _g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().lvl() > _motCfg.osmBuildOpts.maxSnapLevel) continue; + // don't snap to one way edges + if (e->pl().oneWay() == 2) continue; + + _eGrid.add(*e->pl().getGeom(), e); + } + } + + for (auto* n : _g->getNds()) { + // only station nodes + if (n->pl().getSI()) { + _nGrid.add(*n->pl().getGeom(), n); + } + } } // _____________________________________________________________________________ -LINE ShapeBuilder::shapeL(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) { - try { - const router::EdgeListHops& res = route(ncr, rAttrs); +void ShapeBuilder::buildCandCache(const TripForests& forests) { + std::set stops; + size_t count = 0; - 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()); + for (const auto& forest : forests) { + for (const auto& trie : forest.second) { + for (const auto& trips : trie.getNdTrips()) { + for (const auto& st : trips.second[0]->getStopTimes()) { + stops.insert(st.getStop()); } - last = e->getOtherNd(last); } } + } - return l; + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector caches(numThreads); + std::vector> threadStops(numThreads); + + size_t i = 0; + for (auto stop : stops) { + threadStops[i].push_back(stop); + if (++i == numThreads) i = 0; + } + + i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::edgCandWorker, this, &threadStops[i], + &caches[i]); + i++; + } + + for (auto& thr : thrds) thr.join(); + + // merge + for (size_t i = 0; i < numThreads; i++) { + for (const auto& c : caches[i]) { + _grpCache[c.first] = c.second; + count += c.second.size(); + } + } + + if (_grpCache.size()) + LOG(DEBUG) << "Average candidate set size: " + << ((count * 1.0) / _grpCache.size()); +} + +// _____________________________________________________________________________ +EdgeCandGroup ShapeBuilder::getEdgCands(const Stop* s) const { + auto cached = _grpCache.find(s); + if (cached != _grpCache.end()) return cached->second; + + EdgeCandGroup ret; + + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; + auto normedName = snormzer.norm(s->getName()); + + // the first cand is a placeholder for the stop position itself, it is chosen + // when no candidate yielded a feasible route + auto pos = POINT(s->getLng(), s->getLat()); + ret.push_back({0, 0, 0, pos, 0, {}}); + + // unsigned seed = + // std::chrono::system_clock::now().time_since_epoch().count(); + // std::default_random_engine gen(seed); + // std::normal_distribution dist(0.0, 25.0); + + // add some gaussian noise + // pos.setX(pos.getX() + dist(gen)); + // pos.setY(pos.getY() + dist(gen)); + + double maxMDist = _motCfg.osmBuildOpts.maxStationCandDistance; + + double distor = util::geo::latLngDistFactor(pos); + + std::set frNIdx; + _nGrid.get(util::geo::pad(util::geo::getBoundingBox(pos), + (maxMDist / M_PER_DEG) / distor), + &frNIdx); + + if (_motCfg.routingOpts.useStations) { + for (auto nd : frNIdx) { + assert(nd->pl().getSI()); + + double mDist = util::geo::haversine(pos, *nd->pl().getGeom()); + if (mDist > maxMDist) continue; + + double nameMatchPunish = 0; + double trackMatchPunish = 0; + + if (!_classifier->similar(normedName, pos, nd->pl().getSI()->getName(), + *nd->pl().getGeom())) { + // stations do not match, punish + nameMatchPunish = _motCfg.routingOpts.stationUnmatchedPen; + } + + std::string platform = s->getPlatformCode(); + + if (!platform.empty() && !nd->pl().getSI()->getTrack().empty() && + nd->pl().getSI()->getTrack() == platform) { + trackMatchPunish = _motCfg.routingOpts.platformUnmatchedPen; + } + + for (auto* e : nd->getAdjListOut()) { + // don't snap to one way edges + if (e->pl().oneWay() == 2) continue; + ret.push_back({e, + mDist * _motCfg.routingOpts.stationDistPenFactor + + nameMatchPunish + trackMatchPunish, + 0, + {}, + 0, + {}}); + } + } + } + + maxMDist = _motCfg.osmBuildOpts.maxSnapDistance; + + std::set frEIdx; + _eGrid.get(util::geo::pad(util::geo::getBoundingBox(pos), + (maxMDist / M_PER_DEG) / distor), + &frEIdx); + + std::set selected; + std::map scores; + std::map progrs; + + for (auto edg : frEIdx) { + if (selected.count(edg)) continue; + + auto reach = deg2reachable(edg, selected); + + double mDist = dist(pos, *edg->pl().getGeom()) * distor * M_PER_DEG; + + if (mDist > maxMDist) continue; + + if (!reach || mDist < scores[reach]) { + if (reach) { + selected.erase(selected.find(reach)); + scores.erase(scores.find(reach)); + } + util::geo::PolyLine pl(*edg->pl().getGeom()); + auto lp = pl.projectOn(pos); + double progr = lp.totalPos; + if (edg->pl().isRev()) progr = 1 - progr; + selected.insert(edg); + scores[edg] = mDist; + progrs[edg] = progr; + } + } + + for (auto e : selected) { + ret.push_back({e, + scores[e] * _motCfg.routingOpts.stationDistPenFactor + + _motCfg.routingOpts.nonStationPen, + progrs[e], + {}, + 0, + {}}); + } + + return ret; +} + +// _____________________________________________________________________________ +pfaedle::trgraph::Edge* ShapeBuilder::deg2reachable( + trgraph::Edge* e, std::set edgs) const { + trgraph::Edge* cur = e; + + // forward + while (cur->getTo()->getDeg() == 2) { + // dont allow backtracking on reverse edge + auto next = e->getTo()->getAdjListOut().front()->getTo() == e->getFrom() + ? e->getTo()->getAdjListOut().back() + : e->getTo()->getAdjListOut().front(); + if (next == e || next == cur) break; // avoid circles + if (next->pl().oneWay() == 2) break; // dont follow one way edges + if (edgs.count(next)) return next; + cur = next; + } + + // backward + while (cur->getFrom()->getDeg() == 2) { + // dont allow backtracking on reverse edge + auto next = e->getFrom()->getAdjListIn().front()->getFrom() == e->getTo() + ? e->getFrom()->getAdjListIn().back() + : e->getFrom()->getAdjListIn().front(); + if (next == e || next == cur) break; // avoid circles + if (next->pl().oneWay() == 2) break; // dont follow one way edges + if (edgs.count(cur)) return next; + cur = next; + } + + return 0; +} + +// _____________________________________________________________________________ +std::pair, Stats> ShapeBuilder::shapeL(Trip* trip) { + Stats stats; + try { + T_START(t); + EDijkstra::ITERS = 0; + auto hops = shapeify(trip); + stats.solveTime = T_STOP(t); + stats.numTries = 1; + stats.numTrieLeafs = 1; + stats.totNumTrips = 1; + stats.dijkstraIters = EDijkstra::ITERS; + std::map colors; + LOG(INFO) << "Matched 1 trip in " << stats.solveTime << " ms."; + // print to line + return {getGeom(hops, getRAttrs(trip), &colors), stats}; } catch (const std::runtime_error& e) { LOG(ERROR) << e.what(); - return LINE(); + return {std::vector(), stats}; } } // _____________________________________________________________________________ -LINE ShapeBuilder::shapeL(Trip* trip) { - return shapeL(getNCR(trip), getRAttrs(trip)); +std::map ShapeBuilder::route( + const TripTrie* trie, const EdgeCandMap& ecm, HopCache* hopCache) const { + return _router->route(trie, ecm, _motCfg.routingOpts, *_restr, hopCache, + _cfg.noFastHops); } // _____________________________________________________________________________ -EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) const { - router::Graph g; +std::map ShapeBuilder::shapeify( + const TripTrie* trie, HopCache* hopCache) const { + LOG(VDEBUG) << "Map-matching trie " << trie; - if (_cfg.solveMethod == "global") { - const router::EdgeListHops& ret = - _crouter.route(ncr, rAttrs, _motCfg.routingOpts, *_restr, &g); + // TODO(patrick): assumes the trie is not empty, check this! + assert(trie->getNdTrips().size()); + assert(trie->getNdTrips().begin()->second.size()); + RoutingAttrs rAttrs = getRAttrs(trie->getNdTrips().begin()->second[0]); - // write combination graph - if (!_cfg.shapeTripId.empty() && _cfg.writeCombGraph) { - LOG(INFO) << "Outputting combgraph.json..."; - std::ofstream pstr(_cfg.dbgOutputPath + "/combgraph.json"); - GeoGraphJsonOutput o; - o.printLatLng(g, pstr); - } + std::map ret; - return ret; - } else if (_cfg.solveMethod == "greedy") { - return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, *_restr); - } else if (_cfg.solveMethod == "greedy2") { - return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, *_restr); - } else { - LOG(ERROR) << "Unknown solution method " << _cfg.solveMethod; - exit(1); + const auto& routes = route(trie, getECM(trie), hopCache); + + for (const auto& route : routes) { + ret[route.first] = route.second; } - return EdgeListHops(); -} - -// _____________________________________________________________________________ -pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) const { - LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot " - << trip->getRoute()->getType() << "(sn=" << trip->getShortname() - << ", rsn=" << trip->getRoute()->getShortName() - << ", rln=" << trip->getRoute()->getLongName() << ")"; - Shape ret; - ret.hops = route(getNCR(trip), getRAttrs(trip)); - ret.avgHopDist = avgHopDist(trip); - - LOG(VDEBUG) << "Finished map-matching for #" << trip->getId(); + LOG(VDEBUG) << "Finished map-matching for trie " << trie; return ret; } // _____________________________________________________________________________ -pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) { +EdgeListHops ShapeBuilder::shapeify(Trip* trip) { LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot " << trip->getRoute()->getType() << "(sn=" << trip->getShortname() << ", rsn=" << trip->getRoute()->getShortName() << ", rln=" << trip->getRoute()->getLongName() << ")"; + TripTrie trie; + trie.addTrip(trip, getRAttrs(trip), + _motCfg.routingOpts.transPenMethod == "timenorm", false); + const auto& routes = route(&trie, getECM(&trie), 0); - Shape ret; - ret.hops = route(getNCR(trip), getRAttrs(trip)); - ret.avgHopDist = avgHopDist(trip); - - LOG(VDEBUG) << "Finished map-matching for #" << trip->getId(); - - return ret; + return routes.begin()->second; } // _____________________________________________________________________________ -void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { - TrGraphEdgs gtfsGraph; +Stats ShapeBuilder::shapeify(pfaedle::netgraph::Graph* outNg) { + Stats stats; + EDijkstra::ITERS = 0; + T_START(cluster); LOG(DEBUG) << "Clustering trips..."; - Clusters clusters = clusterTrips(_feed, _mots); - LOG(DEBUG) << "Clustered trips into " << clusters.size() << " clusters."; + const TripForests& forests = clusterTrips(_feed, _mots); + for (const auto& forest : forests) { + for (const auto& trie : forest.second) { + stats.numTries++; + stats.numTrieLeafs += trie.getNdTrips().size(); + } + } + LOG(DEBUG) << "Clustered trips into " << stats.numTries + << " tries with a total of " << stats.numTrieLeafs << " leafs in " + << T_STOP(cluster) << "ms"; + + LOG(DEBUG) << "Building candidate cache..."; + buildCandCache(forests); + LOG(DEBUG) << "Done."; + + std::map shpUse; + RouteRefColors refColors; - std::map shpUsage; for (auto t : _feed->getTrips()) { - if (!t.getShape().empty()) shpUsage[t.getShape()]++; - } + if (!t.getShape().empty()) shpUse[t.getShape()]++; - // to avoid unfair load balance on threads - std::random_shuffle(clusters.begin(), clusters.end()); + // write the colors of trips we won't touch, but whose route we might + if (t.getStopTimes().size() < 2) continue; + if (!_mots.count(t.getRoute()->getType()) || + !_motCfg.mots.count(t.getRoute()->getType())) + continue; - size_t iters = EDijkstra::ITERS; - size_t totiters = EDijkstra::ITERS; - size_t oiters = EDijkstra::ITERS; - size_t j = 0; - - auto t1 = TIME(); - auto t2 = TIME(); - double totAvgDist = 0; - size_t totNumTrips = 0; - -#pragma omp parallel for num_threads(_numThreads) - for (size_t i = 0; i < clusters.size(); i++) { - j++; - - if (j % 10 == 0) { -#pragma omp critical - { - LOG(INFO) << "@ " << j << " / " << clusters.size() << " (" - << (static_cast((j * 1.0) / clusters.size() * 100)) - << "%, " << (EDijkstra::ITERS - oiters) << " iters, " - << "matching " << (10.0 / (TOOK(t1, TIME()) / 1000)) - << " trips/sec)"; - - oiters = EDijkstra::ITERS; - t1 = TIME(); - } - } - - // explicitly call const version of shape here for thread safety - const Shape& cshp = - const_cast(*this).shape(clusters[i][0]); - totAvgDist += cshp.avgHopDist; - - if (_cfg.buildTransitGraph) { -#pragma omp critical - { writeTransitGraph(cshp, >fsGraph, clusters[i]); } - } - - std::vector distances; - const ad::cppgtfs::gtfs::Shape& shp = - getGtfsShape(cshp, clusters[i][0], &distances); - - LOG(VDEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations."; - iters = EDijkstra::ITERS; - - totNumTrips += clusters[i].size(); - - for (auto t : clusters[i]) { - if (_cfg.evaluate && _evalFeed && _ecoll) { - std::lock_guard guard(_shpMutex); - _ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp, - distances); - } - - if (!t->getShape().empty() && shpUsage[t->getShape()] > 0) { - shpUsage[t->getShape()]--; - if (shpUsage[t->getShape()] == 0) { - std::lock_guard guard(_shpMutex); - _feed->getShapes().remove(t->getShape()); - } - } - setShape(t, shp, distances); + if (!t.getShape().empty() && !_cfg.dropShapes) { + refColors[t.getRoute()][t.getRoute()->getColor()].push_back(&t); } } - LOG(INFO) << "Matched " << totNumTrips << " trips in " << clusters.size() - << " clusters."; - LOG(DEBUG) << "Took " << (EDijkstra::ITERS - totiters) - << " iterations in total."; - LOG(DEBUG) << "Took " << TOOK(t2, TIME()) << " ms in total."; - LOG(DEBUG) << "Total avg. tput " - << (static_cast(EDijkstra::ITERS - totiters)) / - TOOK(t2, TIME()) - << " iters/sec"; - LOG(DEBUG) << "Total avg. trip tput " - << (clusters.size() / (TOOK(t2, TIME()) / 1000)) << " trips/sec"; - LOG(DEBUG) << "Avg hop distance was " - << (totAvgDist / static_cast(clusters.size())) - << " meters"; + // we implicitely cluster by routing attrs here. This ensures that now two + // threads will access the same routing attrs later on, which safes us an + // expensive locking mechanism later on for the hop cache + std::vector tries; + for (const auto& forest : forests) { + tries.push_back(&(forest.second)); + for (const auto& trie : forest.second) { + for (const auto& trips : trie.getNdTrips()) { + stats.totNumTrips += trips.second.size(); + } + } + } + + auto tStart = TIME(); + std::atomic at(0); + + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector colors(numThreads); + std::vector gtfsGraphs(numThreads); + + size_t i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::shapeWorker, this, &tries, &at, &shpUse, + &colors[i], >fsGraphs[i]); + i++; + } + + for (auto& thr : thrds) thr.join(); + + stats.solveTime = TOOK(tStart, TIME()); + + LOG(INFO) << "Matched " << stats.totNumTrips << " trips in " + << stats.solveTime << " ms."; + + // merge colors + for (auto& cols : colors) { + for (auto& route : cols) { + for (auto& col : route.second) { + refColors[route.first][col.first].insert( + refColors[route.first][col.first].end(), col.second.begin(), + col.second.end()); + } + } + } + + // update them in the routes, split routes if necessary + updateRouteColors(refColors); if (_cfg.buildTransitGraph) { - LOG(INFO) << "Building transit network graph..."; - buildTrGraph(>fsGraph, ng); + LOG(DEBUG) << "Building transit network graph..."; + + // merge gtfsgraph from threads + TrGraphEdgs gtfsGraph; + + for (auto& g : gtfsGraphs) { + for (auto& ePair : g) { + gtfsGraph[ePair.first].insert(gtfsGraph[ePair.first].begin(), + ePair.second.begin(), ePair.second.end()); + } + } + buildNetGraph(>fsGraph, outNg); + } + + stats.dijkstraIters = EDijkstra::ITERS; + + return stats; +} + +// _____________________________________________________________________________ +void ShapeBuilder::updateRouteColors(const RouteRefColors& refColors) { + for (auto& route : refColors) { + if (route.second.size() == 1) { + // only one color found for this route, great! + // update inplace... + route.first->setColor(route.second.begin()->first); + if (route.first->getColor() != NO_COLOR) + route.first->setTextColor(getTextColor(route.first->getColor())); + } else { + // are there fare rules using this route? + std::vector< + std::pair*, + ad::cppgtfs::gtfs::FareRule>> + rules; + + for (auto& f : _feed->getFares()) { + for (auto r : f.second->getFareRules()) { + if (r.getRoute() == route.first) { + rules.push_back({f.second, r}); + } + } + } + + // add new routes... + for (auto& c : route.second) { + // keep the original one intact + if (c.first == route.first->getColor()) continue; + + auto routeCp = *route.first; + + // find free id + std::string newId = route.first->getId() + "::1"; + size_t i = 1; + while (_feed->getRoutes().get(newId)) { + i++; + newId = route.first->getId() + "::" + std::to_string(i); + } + + routeCp.setId(newId); + routeCp.setColor(c.first); + routeCp.setTextColor(getTextColor(routeCp.getColor())); + + auto newRoute = _feed->getRoutes().add(routeCp); + + // update trips to use that route + for (auto& t : c.second) t->setRoute(newRoute); + + // add new fare rules + for (auto a : rules) { + auto rule = a.second; + rule.setRoute(newRoute); + a.first->addFareRule(rule); + } + } + } } } // _____________________________________________________________________________ void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, - const std::vector& distances) { + const std::vector& distances) { assert(distances.size() == t->getStopTimes().size()); // set distances size_t i = 0; @@ -291,91 +547,44 @@ void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, } std::lock_guard guard(_shpMutex); - t->setShape(_feed->getShapes().add(s)); + auto gtfsShp = _feed->getShapes().add(s); + t->setShape(gtfsShp); } // _____________________________________________________________________________ ad::cppgtfs::gtfs::Shape ShapeBuilder::getGtfsShape( - const Shape& shp, Trip* t, std::vector* hopDists) { + const EdgeListHops& hops, Trip* t, const RoutingAttrs& rAttrs, + std::vector* hopDists, uint32_t* bestColor) { ad::cppgtfs::gtfs::Shape ret(getFreeShapeId(t)); - assert(shp.hops.size() == t->getStopTimes().size() - 1); + assert(hops.size() == t->getStopTimes().size() - 1); + + std::map colors; + + const std::vector& gl = getGeom(hops, rAttrs, &colors); + const std::vector& measures = getMeasure(gl); size_t seq = 0; - double dist = -1; - double lastDist = -1; hopDists->push_back(0); - POINT last(0, 0); - for (const auto& hop : shp.hops) { - const trgraph::Node* l = hop.start; - if (hop.edges.size() == 0) { - POINT ll = webMercToLatLng( - hop.start->pl().getGeom()->getX(), hop.start->pl().getGeom()->getY()); - - if (dist > -0.5) - dist += webMercMeterDist(last, *hop.start->pl().getGeom()); - else - dist = 0; - - last = *hop.start->pl().getGeom(); - - if (dist - lastDist > 0.01) { - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - - dist += webMercMeterDist(last, *hop.end->pl().getGeom()); - last = *hop.end->pl().getGeom(); - - if (dist - lastDist > 0.01) { - ll = webMercToLatLng( - hop.end->pl().getGeom()->getX(), hop.end->pl().getGeom()->getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } + for (size_t i = 0; i < gl.size(); i++) { + for (size_t j = 0; j < gl[i].size(); j++) { + ret.addPoint( + ShapePoint(gl[i][j].getY(), gl[i][j].getX(), measures[seq], seq)); + seq++; } + hopDists->push_back(measures[seq - 1]); + } - for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) { - const auto* e = *i; - if ((e->getFrom() == l) ^ e->pl().isRev()) { - for (size_t i = 0; i < e->pl().getGeom()->size(); i++) { - const POINT& cur = (*e->pl().getGeom())[i]; - if (dist > -0.5) - dist += webMercMeterDist(last, cur); - else - dist = 0; - last = cur; - if (dist - lastDist > 0.01) { - POINT ll = - webMercToLatLng(cur.getX(), cur.getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - } - } else { - for (int64_t i = e->pl().getGeom()->size() - 1; i >= 0; i--) { - const POINT& cur = (*e->pl().getGeom())[i]; - if (dist > -0.5) - dist += webMercMeterDist(last, cur); - else - dist = 0; - last = cur; - if (dist - lastDist > 0.01) { - POINT ll = - webMercToLatLng(cur.getX(), cur.getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - } - } - l = e->getOtherNd(l); + // get most likely color + double best = 0; + *bestColor = NO_COLOR; + for (const auto& c : colors) { + double progr = c.second / measures.back(); + // TODO(patrick): make threshold configurable + if (progr > 0.9 && progr > best) { + best = progr; + *bestColor = c.first; } - - hopDists->push_back(lastDist); } return ret; @@ -402,21 +611,24 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) { if (i == _rAttrs.end()) { router::RoutingAttrs ret; + ret.classifier = _classifier; + const auto& lnormzer = _motCfg.osmBuildOpts.lineNormzer; + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; ret.shortName = lnormzer.norm(trip->getRoute()->getShortName()); + ret.lineFrom = + snormzer.norm(trip->getStopTimes().front().getStop()->getName()); + ret.lineTo = { + snormzer.norm(trip->getStopTimes().back().getStop()->getName())}; + // fallbacks for line name if (ret.shortName.empty()) ret.shortName = lnormzer.norm(trip->getShortname()); if (ret.shortName.empty()) ret.shortName = lnormzer.norm(trip->getRoute()->getLongName()); - ret.fromString = _motCfg.osmBuildOpts.statNormzer.norm( - trip->getStopTimes().begin()->getStop()->getName()); - ret.toString = _motCfg.osmBuildOpts.statNormzer.norm( - (--trip->getStopTimes().end())->getStop()->getName()); - return _rAttrs .insert(std::pair(trip, ret)) .first->second; @@ -433,7 +645,7 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const { // _____________________________________________________________________________ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, const std::string& tid, bool dropShapes, - osm::BBoxIdx* box) { + osm::BBoxIdx* box, double maxSpeed) { for (const auto& t : feed->getTrips()) { if (!tid.empty() && t.getId() != tid) continue; if (tid.empty() && !t.getShape().empty() && !dropShapes) continue; @@ -441,7 +653,49 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, if (mots.count(t.getRoute()->getType())) { DBox cur; - for (const auto& st : t.getStopTimes()) { + for (size_t i = 0; i < t.getStopTimes().size(); i++) { + // skip outliers + const auto& st = t.getStopTimes()[i]; + + int toTime = std::numeric_limits::max(); + double toD = 0; + int fromTime = std::numeric_limits::max(); + double fromD = 0; + + if (i > 0) { + const auto& stPrev = t.getStopTimes()[i - 1]; + toTime = st.getArrivalTime().seconds() - + stPrev.getDepartureTime().seconds(); + toD = util::geo::haversine( + st.getStop()->getLat(), st.getStop()->getLng(), + stPrev.getStop()->getLat(), stPrev.getStop()->getLng()); + } + + if (i < t.getStopTimes().size() - 1) { + const auto& stNext = t.getStopTimes()[i + 1]; + fromTime = stNext.getArrivalTime().seconds() - + st.getDepartureTime().seconds(); + fromD = util::geo::haversine( + st.getStop()->getLat(), st.getStop()->getLng(), + stNext.getStop()->getLat(), stNext.getStop()->getLng()); + } + + const double reqToTime = toD / maxSpeed; + const double reqFromTime = fromD / maxSpeed; + + const double BUFFER = 5 * 60; + + if (reqToTime > (BUFFER + toTime) * 3 * MAX_ROUTE_COST_DOUBLING_STEPS && + reqFromTime > + (BUFFER + fromTime) * 3 * MAX_ROUTE_COST_DOUBLING_STEPS) { + LOG(DEBUG) << "Skipping station " << st.getStop()->getId() << " (" + << st.getStop()->getName() << ") @ " + << st.getStop()->getLat() << ", " << st.getStop()->getLng() + << " for bounding box as the vehicle cannot realistically " + "reach and leave it in the scheduled time"; + continue; + } + cur = extendBox(DPoint(st.getStop()->getLng(), st.getStop()->getLat()), cur); } @@ -451,141 +705,224 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, } // _____________________________________________________________________________ -NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const { - router::NodeCandRoute ncr(trip->getStopTimes().size()); +std::vector ShapeBuilder::getTransTimes(Trip* trip) const { + std::vector ret; - size_t i = 0; + for (size_t i = 0; i < trip->getStopTimes().size() - 1; i++) { + auto cur = trip->getStopTimes()[i]; + auto next = trip->getStopTimes()[i + 1]; - 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; -} + int depTime = cur.getDepartureTime().seconds(); + int arrTime = next.getArrivalTime().seconds(); -// _____________________________________________________________________________ -double ShapeBuilder::avgHopDist(Trip* trip) const { - size_t i = 0; - double sum = 0; + int diff = arrTime - depTime; + if (diff < 1) diff = 1; - const Stop* prev = 0; - - for (const auto& st : trip->getStopTimes()) { - if (!prev) { - prev = st.getStop(); - continue; - } - auto a = util::geo::latLngToWebMerc(prev->getLat(), - prev->getLng()); - auto b = util::geo::latLngToWebMerc( - st.getStop()->getLat(), st.getStop()->getLng()); - sum += util::geo::webMercMeterDist(a, b); - - prev = st.getStop(); - i++; - } - return sum / static_cast(i); -} - -// _____________________________________________________________________________ -Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { - // building an index [start station, end station] -> [cluster] - - std::map> clusterIdx; - - Clusters ret; - 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.getStopTimes().begin()->getStop(), - trip.getStopTimes().rbegin()->getStop()); - const auto& c = clusterIdx[spair]; - - for (size_t i = 0; i < c.size(); i++) { - if (routingEqual(ret[c[i]][0], &trip)) { - ret[c[i]].push_back(&trip); - found = true; - break; - } - } - if (!found) { - ret.push_back(Cluster{&trip}); - // explicit call to write render attrs to cache - getRAttrs(&trip); - clusterIdx[spair].push_back(ret.size() - 1); - } + ret.push_back(diff); + assert(ret.back() >= 0); } return ret; } // _____________________________________________________________________________ -bool ShapeBuilder::routingEqual(const Stop* a, const Stop* b) { - if (a == b) return true; // trivial +std::vector ShapeBuilder::getTransDists(Trip* trip) const { + std::vector ret; - auto namea = _motCfg.osmBuildOpts.statNormzer.norm(a->getName()); - auto nameb = _motCfg.osmBuildOpts.statNormzer.norm(b->getName()); - if (namea != nameb) return false; + for (size_t i = 0; i < trip->getStopTimes().size() - 1; i++) { + auto cur = trip->getStopTimes()[i]; + auto next = trip->getStopTimes()[i + 1]; - auto tracka = _motCfg.osmBuildOpts.trackNormzer.norm(a->getPlatformCode()); - auto trackb = _motCfg.osmBuildOpts.trackNormzer.norm(b->getPlatformCode()); - if (tracka != trackb) return false; + double dist = util::geo::haversine( + cur.getStop()->getLat(), cur.getStop()->getLng(), + next.getStop()->getLat(), next.getStop()->getLng()); - POINT ap = - util::geo::latLngToWebMerc(a->getLat(), a->getLng()); - POINT bp = - util::geo::latLngToWebMerc(b->getLat(), b->getLng()); + ret.push_back(dist); + } - double d = util::geo::webMercMeterDist(ap, bp); - - if (d > 1) return false; - - return true; + return ret; } // _____________________________________________________________________________ -bool ShapeBuilder::routingEqual(Trip* a, Trip* b) { - if (a->getStopTimes().size() != b->getStopTimes().size()) return false; - if (getRAttrs(a) != getRAttrs(b)) return false; +EdgeCandMap ShapeBuilder::getECM(const TripTrie* trie) const { + EdgeCandMap ecm(trie->getNds().size()); - auto stb = b->getStopTimes().begin(); - for (const auto& sta : a->getStopTimes()) { - if (!routingEqual(sta.getStop(), stb->getStop())) { - return false; + for (size_t nid = 1; nid < trie->getNds().size(); nid++) { + auto trNd = trie->getNds()[nid]; + auto parentTrNd = trie->getNds()[trNd.parent]; + + if (nid != 1 && !trNd.arr) continue; + + double avgT = 0; + + if (trNd.trips) avgT = trNd.accTime / trNd.trips; + + const auto& cands = getEdgCands(trNd.reprStop); + ecm[nid].reserve(cands.size()); + + for (auto& cand : cands) { + const auto& timeExpCands = timeExpand(cand, avgT); + assert(timeExpCands.size()); + + for (size_t depChildId : trNd.childs) { + if (nid == 1) break; + auto chldTrNd = trie->getNds()[depChildId]; + double avgChildT = 0; + if (chldTrNd.trips) avgChildT = chldTrNd.accTime / chldTrNd.trips; + + double timeDiff = avgChildT - avgT; + if (timeDiff < 0) timeDiff = 0; + + for (size_t candId = 0; candId < timeExpCands.size(); candId++) { + const auto& cand = timeExpCands[candId]; + ecm[depChildId].push_back(cand); + ecm[depChildId].back().time += timeDiff; + + ecm[depChildId].back().pen = timePen(cand.time, avgChildT); + + for (size_t sucCandId = 0; sucCandId < timeExpCands.size(); + sucCandId++) { + if (timeExpCands[sucCandId].time <= ecm[depChildId].back().time) { + ecm[depChildId].back().depPrede.push_back(sucCandId + + ecm[nid].size()); + } + } + assert(ecm[depChildId].back().depPrede.size()); + } + } + ecm[nid].insert(ecm[nid].end(), timeExpCands.begin(), timeExpCands.end()); } - stb++; + + assert(ecm[nid].size() != 0); } - return true; + return ecm; +} + +// _____________________________________________________________________________ +double ShapeBuilder::timePen(int candTime, int schedTime) const { + // standard deviation of normal distribution + double standarddev = 5 * 60; + + int diff = abs(candTime - schedTime); + + double cNorm = (diff) / standarddev; + return cNorm * cNorm; +} + +// _____________________________________________________________________________ +EdgeCandGroup ShapeBuilder::timeExpand(const EdgeCand& ec, int time) const { + EdgeCandGroup ret; + // TODO(patrick): heuristic for time expansion variance + // for (int i = -5; i < 6; i++) { + // for (int i = -10; i < 1; i++) { + for (int i = 0; i < 1; i++) { + EdgeCand ecNew = ec; + // in 30 sec steps + ecNew.time = time + i * 30; + ecNew.pen = ecNew.pen + timePen(ecNew.time, time); + ret.push_back(ecNew); + } + + return ret; +} + +// _____________________________________________________________________________ +TripForests ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { + TripForests forest; + std::map> trips; + + // warm the stop name normalizer caches so a + // multithreaded access later on will never write to the underlying cache + for (auto& stop : f->getStops()) { + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; + auto normedName = snormzer.norm(stop.getName()); + } + + // cluster by routing attr for parallization later on + for (auto& trip : f->getTrips()) { + if (!_cfg.dropShapes && !trip.getShape().empty()) continue; + if (trip.getStopTimes().size() < 2) continue; + if (!mots.count(trip.getRoute()->getType()) || + !_motCfg.mots.count(trip.getRoute()->getType())) + continue; + + // important: we are building the routing attributes here, so a + // multithreaded access later on will never write to the underlying cache + const auto& rAttrs = getRAttrs(&trip); + + trips[rAttrs].push_back(&trip); + forest[rAttrs] = {}; + } + + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector> attrs(numThreads); + + size_t i = 0; + for (auto it : trips) { + attrs[i].push_back(it.first); + if (++i == numThreads) i = 0; + } + + i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::clusterWorker, this, &attrs[i], &trips, + &forest); + i++; + } + + for (auto& thr : thrds) thr.join(); + + return forest; +} + +// _____________________________________________________________________________ +void ShapeBuilder::clusterWorker( + const std::vector* rAttrsVec, + const std::map>* trips, + TripForests* forest) { + for (const auto& rAttrs : *rAttrsVec) { + for (auto& trip : trips->at(rAttrs)) { + bool ins = false; + auto& subForest = forest->at(rAttrs); + for (auto& trie : subForest) { + if (trie.addTrip(trip, rAttrs, + _motCfg.routingOpts.transPenMethod == "timenorm", + _cfg.noTrie)) { + ins = true; + break; + } + } + + if (!ins) { + subForest.resize(subForest.size() + 1); + subForest.back().addTrip( + trip, rAttrs, _motCfg.routingOpts.transPenMethod == "timenorm", + false); + } + } + } } // _____________________________________________________________________________ const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return _g; } // _____________________________________________________________________________ -void ShapeBuilder::writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, - const Cluster& cluster) const { - for (auto hop : shp.hops) { +void ShapeBuilder::writeTransitGraph( + const router::EdgeListHops& hops, TrGraphEdgs* edgs, + const std::vector& trips) const { + for (const auto& hop : hops) { for (const auto* e : hop.edges) { if (e->pl().isRev()) e = _g->getEdg(e->getTo(), e->getFrom()); - (*edgs)[e].insert(cluster.begin(), cluster.end()); + (*edgs)[e].insert((*edgs)[e].begin(), trips.begin(), trips.end()); } } } // _____________________________________________________________________________ -void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs, - pfaedle::netgraph::Graph* ng) const { +void ShapeBuilder::buildNetGraph(TrGraphEdgs* edgs, + pfaedle::netgraph::Graph* ng) const { std::unordered_map nodes; for (auto ep : *edgs) { @@ -607,3 +944,275 @@ void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::EdgePL(*e->pl().getGeom(), ep.second)); } } + +// _____________________________________________________________________________ +std::vector ShapeBuilder::getGeom( + const EdgeListHops& hops, const RoutingAttrs& rAttrs, + std::map* colors) const { + std::vector ret; + + for (size_t i = hops.size(); i > 0; i--) { + const auto& hop = hops[i - 1]; + if (!hop.start || !hop.end) { + // no hop was found, use the fallback geometry + + if (hop.start) { + auto l = getLine(hop.start); + if (hop.progrStart > 0) { + PolyLine pl(l); + const auto& seg = pl.getSegment(hop.progrStart, 1); + ret.push_back({seg.getLine().front(), hop.pointEnd}); + } else { + ret.push_back({*hop.start->getFrom()->pl().getGeom(), hop.pointEnd}); + } + } else if (hop.end) { + auto l = getLine(hop.end); + if (hop.progrEnd > 0) { + PolyLine pl(l); + const auto& seg = pl.getSegment(0, hop.progrEnd); + ret.push_back({hop.pointStart, seg.getLine().back()}); + } else { + ret.push_back({hop.pointStart, *hop.end->getFrom()->pl().getGeom()}); + } + } else { + ret.push_back({hop.pointEnd, hop.pointStart}); + } + } else { + const auto& l = getLine(hop, rAttrs, colors); + ret.push_back(l); + } + } + + return ret; +} + +// _____________________________________________________________________________ +LINE ShapeBuilder::getLine(const EdgeListHop& hop, const RoutingAttrs& rAttrs, + std::map* colors) const { + LINE l; + + const auto& curL = getLine(hop.start); + + if (hop.edges.size() == 0) { + // draw direct line between positions on edges + if (hop.progrStart > 0) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, 1); + l.push_back(seg.front()); + } else { + l.push_back(curL.front()); + } + + if (hop.progrEnd > 0) { + PolyLine pl(getLine(hop.end)); + const auto& seg = pl.getSegment(0, hop.progrEnd); + l.push_back(seg.back()); + } else { + l.push_back(*hop.end->getFrom()->pl().getGeom()); + } + + return l; + } + + // special case: start and end are on the same edge! + if (hop.edges.size() == 1 && hop.start == hop.end) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, hop.progrEnd); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += hop.start->pl().getLength(); + } + + return l; + } + + auto from = hop.start->getFrom(); + + if (hop.progrStart > 0) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, 1); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + double l = hop.start->pl().getLength() * (1 - hop.progrStart); + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += l; + } + } else { + l.insert(l.end(), curL.begin(), curL.end()); + + double l = hop.start->pl().getLength(); + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += l; + } + } + + from = hop.start->getOtherNd(from); + + if (hop.edges.size() > 1) { + for (size_t j = hop.edges.size() - 2; j > 0; j--) { + const auto* e = hop.edges[j]; + const auto& curL = getLine(e); + l.insert(l.end(), curL.begin(), curL.end()); + from = e->getOtherNd(from); + + double l = e->pl().getLength(); + for (const auto& color : getColorMatch(e, rAttrs)) { + (*colors)[color] += l; + } + } + } + + if (hop.progrEnd > 0) { + PolyLine pl(getLine(hop.end)); + const auto& seg = pl.getSegment(0, hop.progrEnd); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + double l = hop.end->pl().getLength() * hop.progrEnd; + for (const auto& color : getColorMatch(hop.end, rAttrs)) { + (*colors)[color] += l; + } + } + + if (l.size() > 1) return util::geo::simplify(l, 0.5 / M_PER_DEG); + return l; +} + +// _____________________________________________________________________________ +LINE ShapeBuilder::getLine(const trgraph::Edge* e) const { + LINE l; + if (!e->pl().getGeom() || e->pl().getGeom()->size() == 0) + return {*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom()}; + if (e->pl().isRev()) { + l.insert(l.end(), e->pl().getGeom()->rbegin(), e->pl().getGeom()->rend()); + } else { + l.insert(l.end(), e->pl().getGeom()->begin(), e->pl().getGeom()->end()); + } + return l; +} + +// _____________________________________________________________________________ +std::vector ShapeBuilder::getMeasure( + const std::vector& lines) const { + assert(lines.size()); + assert(lines.front().size()); + std::vector ret; + POINT last = lines.front().front(); + + for (const auto& l : lines) { + for (size_t i = 0; i < l.size(); i++) { + if (ret.size() == 0) { + ret.push_back(0); + } else { + float v = ret.back() + util::geo::haversine(last, l[i]); + assert(v >= ret.back()); // required by GTFS standard! + ret.push_back(v); + } + last = l[i]; + } + } + + return ret; +} + +// _____________________________________________________________________________ +void ShapeBuilder::shapeWorker( + const std::vector* tries, std::atomic* at, + std::map* shpUse, + std::map>>* routeColors, + TrGraphEdgs* gtfsGraph) { + while (1) { + size_t j = (*at)++; + if (j >= tries->size()) return; + + int step = tries->size() < 10 ? tries->size() : 10; + + if (j % (tries->size() / step) == 0) { + LOG(INFO) << "@ " << (static_cast((j * 1.0) / tries->size() * 100)) + << "%"; + LOG(DEBUG) << "(@ trie forest " << j << "/" << tries->size() << ")"; + } + + const auto& forest = *((*tries)[j]); + + // hop cache per forest, thus per routing attributes + HopCache hopCacheLoc; + HopCache* hopCache = 0; + + if (!_cfg.noHopCache) hopCache = &hopCacheLoc; + + for (size_t i = 0; i < forest.size(); i++) { + const TripTrie* trie = &(forest[i]); + const auto& hops = shapeify(trie, hopCache); + + for (const auto& leaf : trie->getNdTrips()) { + std::vector distances; + const RoutingAttrs& rAttrs = trie->getNd(leaf.first).rAttrs; + + uint32_t color; + + const ad::cppgtfs::gtfs::Shape& shp = getGtfsShape( + hops.at(leaf.first), leaf.second[0], rAttrs, &distances, &color); + + if (_cfg.buildTransitGraph) { + writeTransitGraph(hops.at(leaf.first), gtfsGraph, leaf.second); + } + + for (auto t : leaf.second) { + if (_cfg.writeColors && color != NO_COLOR && + t->getRoute()->getColor() == NO_COLOR && + t->getRoute()->getTextColor() == NO_COLOR) { + (*routeColors)[t->getRoute()][color].push_back(t); + } else { + // else, use the original route color + (*routeColors)[t->getRoute()][t->getRoute()->getColor()].push_back( + t); + } + + if (!t->getShape().empty() && (*shpUse)[t->getShape()] > 0) { + (*shpUse)[t->getShape()]--; + if ((*shpUse)[t->getShape()] == 0) { + std::lock_guard guard(_shpMutex); + _feed->getShapes().remove(t->getShape()); + } + } + setShape(t, shp, distances); + } + } + } + } +} + +// _____________________________________________________________________________ +void ShapeBuilder::edgCandWorker(std::vector* stops, + GrpCache* cache) { + for (auto stop : *stops) { + (*cache)[stop] = getEdgCands(stop); + } +} + +// _____________________________________________________________________________ +std::set ShapeBuilder::getColorMatch( + const trgraph::Edge* e, const RoutingAttrs& rAttrs) const { + std::set ret; + for (const auto* l : e->pl().getLines()) { + auto simi = rAttrs.simi(l); + if (simi.nameSimilar && l->color != NO_COLOR) ret.insert(l->color); + } + + return ret; +} + +// _____________________________________________________________________________ +uint32_t ShapeBuilder::getTextColor(uint32_t c) const { + double r = (c & 0x00FF0000) >> 16; + double g = (c & 0x0000FF00) >> 8; + double b = (c & 0x000000FF); + + // gray value approx + double a = sqrt((r * r + g * g + b * b) / 3); + + // below a certain gray value, use white, else black + if (a < 140) return 0x00FFFFFF; + return 0; +} diff --git a/src/pfaedle/router/ShapeBuilder.h b/src/pfaedle/router/ShapeBuilder.h index 560d1b2..8087e84 100644 --- a/src/pfaedle/router/ShapeBuilder.h +++ b/src/pfaedle/router/ShapeBuilder.h @@ -9,39 +9,41 @@ #include #include #include +#include #include #include #include "ad/cppgtfs/gtfs/Feed.h" #include "pfaedle/Def.h" #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" #include "pfaedle/router/Router.h" +#include "pfaedle/router/Stats.h" +#include "pfaedle/router/TripTrie.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/Graph.h" #include "util/geo/Geo.h" namespace pfaedle { namespace router { -using ad::cppgtfs::gtfs::Stop; -using pfaedle::gtfs::Trip; -using pfaedle::gtfs::Feed; - -struct Shape { - router::EdgeListHops hops; - double avgHopDist; -}; - -typedef std::vector Cluster; -typedef std::vector Clusters; -typedef std::pair StopPair; -typedef std::unordered_map TripRAttrs; -typedef std::unordered_map> +typedef std::vector TripForest; +typedef std::map TripForests; +typedef std::pair + StopPair; +typedef std::unordered_map + TripRAttrs; +typedef std::unordered_map> TrGraphEdgs; +typedef std::map>> + RouteRefColors; +typedef std::unordered_map + GrpCache; /* * Layer class for the router. Provides an interface for direct usage with @@ -49,76 +51,116 @@ typedef std::unordered_map> */ class ShapeBuilder { public: - ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots, - const config::MotConfig& motCfg, eval::Collector* ecoll, - trgraph::Graph* g, router::FeedStops* stops, - osm::Restrictor* restr, const config::Config& cfg); + ShapeBuilder( + pfaedle::gtfs::Feed* feed, MOTs mots, const config::MotConfig& motCfg, + trgraph::Graph* g, router::FeedStops* stops, osm::Restrictor* restr, + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier, + router::Router* router, const config::Config& cfg); - void shape(pfaedle::netgraph::Graph* ng); + Stats shapeify(pfaedle::netgraph::Graph* outNg); router::FeedStops* getFeedStops(); - const NodeCandGroup& getNodeCands(const Stop* s) const; + // shape single trip + std::pair, Stats> shapeL(pfaedle::gtfs::Trip* trip); - LINE shapeL(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs); - LINE shapeL(Trip* trip); - - pfaedle::router::Shape shape(Trip* trip) const; - pfaedle::router::Shape shape(Trip* trip); + std::map shapeify(const TripTrie* trie, + HopCache* hopCache) const; + EdgeListHops shapeify(pfaedle::gtfs::Trip* trip); const trgraph::Graph* getGraph() const; - static void getGtfsBox(const Feed* feed, const MOTs& mots, + static void getGtfsBox(const pfaedle::gtfs::Feed* feed, const MOTs& mots, const std::string& tid, bool dropShapes, - osm::BBoxIdx* box); + osm::BBoxIdx* box, double maxSpeed); private: - Feed* _feed; - ad::cppgtfs::gtfs::Feed* _evalFeed; + pfaedle::gtfs::Feed* _feed; MOTs _mots; config::MotConfig _motCfg; - eval::Collector* _ecoll; config::Config _cfg; trgraph::Graph* _g; - router::Router _crouter; - router::FeedStops* _stops; - NodeCandGroup _emptyNCG; + EdgeCandGroup _emptyNCG; - size_t _curShpCnt, _numThreads; + size_t _curShpCnt; std::mutex _shpMutex; TripRAttrs _rAttrs; osm::Restrictor* _restr; + const pfaedle::statsimiclassifier::StatsimiClassifier* _classifier; + GrpCache _grpCache; - void buildGraph(router::FeedStops* fStops); + router::Router* _router; - Clusters clusterTrips(Feed* f, MOTs mots); - void writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, - const Cluster& cluster) const; - void buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const; + TripForests clusterTrips(pfaedle::gtfs::Feed* f, MOTs mots); + void buildNetGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const; - std::string getFreeShapeId(Trip* t); + std::string getFreeShapeId(pfaedle::gtfs::Trip* t); + ad::cppgtfs::gtfs::Shape getGtfsShape(const EdgeListHops& shp, + pfaedle::gtfs::Trip* t, + const RoutingAttrs& rAttrs, + std::vector* hopDists, + uint32_t* bestColor); - ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t, - std::vector* hopDists); + void setShape(pfaedle::gtfs::Trip* t, const ad::cppgtfs::gtfs::Shape& s, + const std::vector& dists); - void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, - const std::vector& dists); + EdgeCandGroup getEdgCands(const ad::cppgtfs::gtfs::Stop* s) const; - router::NodeCandRoute getNCR(Trip* trip) const; - double avgHopDist(Trip* trip) const; - const router::RoutingAttrs& getRAttrs(const Trip* trip) const; - const router::RoutingAttrs& getRAttrs(const Trip* trip); - bool routingEqual(Trip* a, Trip* b); - bool routingEqual(const Stop* a, const Stop* b); - router::EdgeListHops route(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) const; + router::EdgeCandMap getECM(const TripTrie* trie) const; + std::vector getTransTimes(pfaedle::gtfs::Trip* trip) const; + std::vector getTransDists(pfaedle::gtfs::Trip* trip) const; + const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip) const; + const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip); + std::map route(const TripTrie* trie, + const EdgeCandMap& ecm, + HopCache* hopCache) const; + void buildCandCache(const TripForests& clusters); + void buildIndex(); + + std::vector getGeom(const EdgeListHops& shp, const RoutingAttrs& rAttrs, + std::map* colors) const; + double timePen(int candTime, int schedTime) const; + + LINE getLine(const EdgeListHop& hop, const RoutingAttrs&, + std::map* colMap) const; + LINE getLine(const trgraph::Edge* edg) const; + std::vector getMeasure(const std::vector& lines) const; + + trgraph::Edge* deg2reachable(trgraph::Edge* e, + std::set edgs) const; + + EdgeCandGroup timeExpand(const EdgeCand& ec, int time) const; + + std::set getColorMatch(const trgraph::Edge* e, + const RoutingAttrs& rAttrs) const; + + void updateRouteColors(const RouteRefColors& c); + + uint32_t getTextColor(uint32_t c) const; + + void writeTransitGraph(const router::EdgeListHops& shp, TrGraphEdgs* edgs, + const std::vector& trips) const; + + void shapeWorker( + const std::vector* tries, std::atomic* at, + std::map* shpUsage, + std::map>>*, + TrGraphEdgs* gtfsGraph); + + void edgCandWorker(std::vector* stops, GrpCache* cache); + void clusterWorker(const std::vector* rAttrs, + const std::map>* trips, + TripForests* forest); + + pfaedle::trgraph::EdgeGrid _eGrid; + pfaedle::trgraph::NodeGrid _nGrid; }; + } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/Stats.h b/src/pfaedle/router/Stats.h new file mode 100644 index 0000000..c01f0e2 --- /dev/null +++ b/src/pfaedle/router/Stats.h @@ -0,0 +1,33 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_STATS_H_ +#define PFAEDLE_ROUTER_STATS_H_ + +#include +#include +#include +#include "util/String.h" + +namespace pfaedle { +namespace router { + +struct Stats { + Stats() + : totNumTrips(0), + numTries(0), + numTrieLeafs(0), + solveTime(0), + dijkstraIters(0) {} + size_t totNumTrips; + size_t numTries; + size_t numTrieLeafs; + double solveTime; + size_t dijkstraIters; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_STATS_H_ diff --git a/src/pfaedle/router/TripTrie.cpp b/src/pfaedle/router/TripTrie.cpp new file mode 100644 index 0000000..470ee09 --- /dev/null +++ b/src/pfaedle/router/TripTrie.cpp @@ -0,0 +1,219 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include "TripTrie.h" +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/gtfs/Feed.h" +#include "pfaedle/gtfs/StopTime.h" +#include "pfaedle/router/TripTrie.h" + +using pfaedle::gtfs::Trip; +using pfaedle::router::TripTrie; + +// _____________________________________________________________________________ +bool TripTrie::addTrip(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx, bool degen) { + if (!degen) return add(trip, rAttrs, timeEx); + + // check if trip is already fully and uniquely contained, if not, fail + size_t existing = get(trip, timeEx); + if (existing && _nds[existing].childs.size() == 0) { + _tripNds[trip] = existing; + _ndTrips[existing].push_back(trip); + return true; + } else { + return false; + } +} + +// _____________________________________________________________________________ +bool TripTrie::add(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx) { + if (trip->getStopTimes().size() == 0) return false; + + int startSecs = trip->getStopTimes().front().getDepartureTime().seconds(); + + size_t curNdId = 0; + for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) { + const auto st = trip->getStopTimes()[stId]; + + std::string name = st.getStop()->getName(); + std::string platform = st.getStop()->getPlatformCode(); + POINT pos = util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()); + + if (stId > 0) { + int arrTime = st.getArrivalTime().seconds() - startSecs; + + size_t arrChild = + getMatchChild(curNdId, name, platform, pos, arrTime, timeEx); + + if (arrChild) { + curNdId = arrChild; + + _nds[arrChild].accTime += arrTime; + _nds[arrChild].trips += 1; + + _nds[arrChild].rAttrs.merge(rAttrs); + } else { + curNdId = insert(st.getStop(), rAttrs, pos, arrTime, true, curNdId); + } + } + + if (stId < trip->getStopTimes().size() - 1) { + int depTime = st.getDepartureTime().seconds() - startSecs; + + size_t depChild = + getMatchChild(curNdId, name, platform, pos, depTime, timeEx); + + if (depChild) { + curNdId = depChild; + + _nds[depChild].accTime += depTime; + _nds[depChild].trips += 1; + + _nds[depChild].rAttrs.merge(rAttrs); + } else { + if (stId == 0 && _tripNds.size() > 0) return false; + curNdId = insert(st.getStop(), rAttrs, pos, depTime, false, curNdId); + } + } + } + + // curNdId is now the last matching node, insert the trip here + _tripNds[trip] = curNdId; + _ndTrips[curNdId].push_back(trip); + + return true; +} + +// _____________________________________________________________________________ +size_t TripTrie::get(pfaedle::gtfs::Trip* trip, bool timeEx) { + if (trip->getStopTimes().size() == 0) return false; + + int startSecs = trip->getStopTimes().front().getDepartureTime().seconds(); + + size_t curNdId = 0; + for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) { + const auto st = trip->getStopTimes()[stId]; + + std::string name = st.getStop()->getName(); + std::string platform = st.getStop()->getPlatformCode(); + POINT pos = util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()); + + if (stId > 0) { + int arrTime = st.getArrivalTime().seconds() - startSecs; + + size_t arrChild = + getMatchChild(curNdId, name, platform, pos, arrTime, timeEx); + + if (arrChild) { + curNdId = arrChild; + } else { + return 0; + } + } + + if (stId < trip->getStopTimes().size() - 1) { + int depTime = st.getDepartureTime().seconds() - startSecs; + + size_t depChild = + getMatchChild(curNdId, name, platform, pos, depTime, timeEx); + + if (depChild) { + curNdId = depChild; + } else { + return 0; + } + } + } + + return curNdId; +} + +// _____________________________________________________________________________ +size_t TripTrie::insert(const ad::cppgtfs::gtfs::Stop* stop, + const RoutingAttrs& rAttrs, const POINT& pos, int time, + bool arr, size_t parent) { + _nds.emplace_back(TripTrieNd{stop, + stop->getName(), + stop->getPlatformCode(), + pos, + stop->getLat(), + stop->getLng(), + time, + arr, + time, + 1, + parent, + {}, + rAttrs}); + _nds[parent].childs.push_back(_nds.size() - 1); + return _nds.size() - 1; +} + +// _____________________________________________________________________________ +const std::vector& TripTrie::getNds() const { + return _nds; +} + +// _____________________________________________________________________________ +size_t TripTrie::getMatchChild(size_t parentNid, const std::string& stopName, + const std::string& platform, POINT pos, int time, + bool timeEx) const { + for (size_t child : _nds[parentNid].childs) { + // TODO(patrick): use similarity classification here? + if (_nds[child].stopName == stopName && _nds[child].platform == platform && + util::geo::dist(_nds[child].pos, pos) < 1 && + (!timeEx || _nds[child].time == time)) { + return child; + } + } + + return 0; +} + +// _____________________________________________________________________________ +void TripTrie::toDot(std::ostream& os, const std::string& rootName, + size_t gid) const { + os << "digraph triptrie" << gid << " {"; + + for (size_t nid = 0; nid < _nds.size(); nid++) { + std::string color = "white"; + if (_ndTrips.count(nid)) color = "red"; + if (nid == 0) { + os << "\"" << gid << ":0\" [label=\"" << rootName << "\"];\n"; + } else { + os << "\"" << gid << ":" << nid + << "\" [shape=\"box\" style=\"filled\" fillcolor=\"" << color + << "\" label=\"#" << nid << ", " << _nds[nid].stopName << "@" + << util::geo::getWKT(_nds[nid].pos) << " t=" << _nds[nid].time + << "\"];\n"; + } + } + + for (size_t nid = 0; nid < _nds.size(); nid++) { + for (size_t child : _nds[nid].childs) { + os << "\"" << gid << ":" << nid << "\" -> \"" << gid << ":" << child + << "\";\n"; + } + } + + os << "}"; +} + +// _____________________________________________________________________________ +const std::map>& +TripTrie::getNdTrips() const { + return _ndTrips; +} + +// _____________________________________________________________________________ +const pfaedle::router::TripTrieNd& TripTrie::getNd(size_t nid) const { + return _nds[nid]; +} diff --git a/src/pfaedle/router/TripTrie.h b/src/pfaedle/router/TripTrie.h new file mode 100644 index 0000000..e03217f --- /dev/null +++ b/src/pfaedle/router/TripTrie.h @@ -0,0 +1,65 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_TRIPTRIE_H_ +#define PFAEDLE_ROUTER_TRIPTRIE_H_ + +#include +#include +#include +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/gtfs/Feed.h" +#include "pfaedle/gtfs/StopTime.h" +#include "pfaedle/router/RoutingAttrs.h" + +namespace pfaedle { +namespace router { + +struct TripTrieNd { + const ad::cppgtfs::gtfs::Stop* reprStop; + std::string stopName; // the stop name at this node + std::string platform; // the platform of node + POINT pos; // the position of this node + double lat, lng; + int time; + bool arr; + int accTime; + size_t trips; + size_t parent; + std::vector childs; + RoutingAttrs rAttrs; +}; + +class TripTrie { + public: + // init node 0, this is the first decision node + TripTrie() : _nds(1) {} + bool addTrip(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx, bool degen); + + const std::vector& getNds() const; + const TripTrieNd& getNd(size_t nid) const; + + void toDot(std::ostream& os, const std::string& rootName, size_t gid) const; + const std::map>& getNdTrips() const; + + private: + std::vector _nds; + std::map _tripNds; + std::map> _ndTrips; + + bool add(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, bool timeEx); + size_t get(pfaedle::gtfs::Trip* trip, bool timeEx); + + size_t getMatchChild(size_t parentNid, const std::string& stopName, + const std::string& platform, POINT pos, int time, + bool timeEx) const; + size_t insert(const ad::cppgtfs::gtfs::Stop* stop, const RoutingAttrs& rAttrs, + const POINT& pos, int time, bool arr, size_t parent); +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_TRIPTRIE_H_ diff --git a/src/pfaedle/router/Weights.cpp b/src/pfaedle/router/Weights.cpp new file mode 100644 index 0000000..70a3aef --- /dev/null +++ b/src/pfaedle/router/Weights.cpp @@ -0,0 +1,261 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include "pfaedle/router/Weights.h" + +using pfaedle::router::DistDiffTransWeight; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::LineSimilarity; +using pfaedle::router::NormDistrTransWeight; +using util::geo::haversine; + +// _____________________________________________________________________________ +ExpoTransWeight::DistHeur::DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) + : _rOpts(rOpts), _maxV(maxV), _maxCentD(0), _lastE(0) { + size_t c = 0; + double x = 0, y = 0; + + for (const auto to : tos) { + x += to->getFrom()->pl().getGeom()->getX(); + y += to->getFrom()->pl().getGeom()->getY(); + c++; + } + + x /= c; + y /= c; + + _center = POINT{x, y}; + + for (const auto to : tos) { + const double cur = haversine(*to->getFrom()->pl().getGeom(), _center); + if (cur > _maxCentD) _maxCentD = cur; + } + + _maxCentD /= _maxV; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::DistHeur::operator()( + const trgraph::Edge* a, const std::set& b) const { + UNUSED(b); + + // avoid repeated calculation for the same edge over and over again + if (a == _lastE) return _lastC; + + _lastE = a; + + const double d = haversine(*a->getFrom()->pl().getGeom(), _center); + const double heur = fmax(0, (d / _maxV - _maxCentD) * 10); + + // avoid overflow + if (heur > std::numeric_limits::max()) { + _lastC = std::numeric_limits::max(); + ; + return _lastC; + } + + _lastC = heur; + return heur; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::CostFunc::operator()(const trgraph::Edge* from, + const trgraph::Node* n, + const trgraph::Edge* to) const { + if (!from) return 0; + + uint32_t c = from->pl().getCost(); + + if (c == std::numeric_limits::max()) return c; + + if (from == _lastFrom) { + // the transit line simi calculation is independent of the "to" edge, so if + // the last "from" edge was the same, skip it! + c = _lastC; + } else if (!_noLineSimiPen) { + const auto& simi = transitLineSimi(from); + + if (!simi.nameSimilar) { + if (_rOpts.lineUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * _rOpts.lineUnmatchedPunishFact); + } else if (_rOpts.lineUnmatchedPunishFact > 1) { + double a = + std::round(static_cast(c) * _rOpts.lineUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + if (!simi.fromSimilar) { + if (_rOpts.lineNameFromUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * + _rOpts.lineNameFromUnmatchedPunishFact); + } else if (_rOpts.lineNameFromUnmatchedPunishFact > 1) { + double a = std::round(static_cast(c) * + _rOpts.lineNameFromUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + if (!simi.toSimilar) { + if (_rOpts.lineNameToUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * + _rOpts.lineNameToUnmatchedPunishFact); + } else if (_rOpts.lineNameToUnmatchedPunishFact > 1) { + double a = std::round(static_cast(c) * + _rOpts.lineNameToUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + _lastC = c; + _lastFrom = from; + } + + uint32_t overflowCheck = c; + + if (n && !n->pl().isTurnCycle()) { + if (_rOpts.fullTurnPunishFac != 0 && from->getFrom() == to->getTo() && + from->getTo() == to->getFrom()) { + // trivial full turn + c += _rOpts.fullTurnPunishFac; + + if (c <= overflowCheck) return std::numeric_limits::max(); + overflowCheck = c; + } else if (_rOpts.fullTurnPunishFac != 0 && n->getDeg() > 2) { + // otherwise, only intersection angles will be punished + + double ang = util::geo::innerProd( + *n->pl().getGeom(), from->pl().backHop(), to->pl().frontHop()); + + if (ang < _rOpts.fullTurnAngle) { + c += _rOpts.fullTurnPunishFac; + if (c <= overflowCheck) return std::numeric_limits::max(); + overflowCheck = c; + } + } + + // turn restriction cost + if (_rOpts.turnRestrCost > 0 && from->pl().isRestricted() && + !_res.may(from, to, n)) { + c += _rOpts.turnRestrCost; + if (c <= overflowCheck) return std::numeric_limits::max(); + } + } + + return c; +} + +// _____________________________________________________________________________ +LineSimilarity ExpoTransWeight::CostFunc::transitLineSimi( + const trgraph::Edge* e) const { + if (_rAttrs.shortName.empty() && _rAttrs.lineFrom.empty() && + _rAttrs.lineTo.empty()) + return {true, true, true}; + + LineSimilarity best = {false, false, false}; + for (const auto* l : e->pl().getLines()) { + auto simi = _rAttrs.simi(l); + if (simi.nameSimilar && simi.toSimilar && simi.fromSimilar) return simi; + if (best < simi) best = simi; + } + + return best; +} + +// _____________________________________________________________________________ +double ExpoTransWeight::weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(t0); + UNUSED(d); + UNUSED(d0); + return rOpts.transitionPen * static_cast(c) / 10.0; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + return std::round((c / rOpts.transitionPen) * 10); +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) { + // abort after 3 times the scheduled time, but assume a min time of + // 1 minute! + return std::ceil(fmax(tTime, 60) * 3 * rOpts.lineUnmatchedPunishFact * + rOpts.lineNameToUnmatchedPunishFact * + rOpts.lineNameFromUnmatchedPunishFact * 10); +} + +// _____________________________________________________________________________ + +// _____________________________________________________________________________ +double NormDistrTransWeight::weight(uint32_t cs, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(d); + UNUSED(d0); + UNUSED(rOpts); + + double t = static_cast(cs) / 10.0; + + // standard deviation of normal distribution + double standarddev = 1; + + // no backwards time travel! + if (t0 < 0) return std::numeric_limits::infinity(); + + // always assume it takes at least 10 seconds to travel + t0 = fmax(10, t0); + + double cNorm = (t / t0 - 1) / standarddev; + double normWeight = cNorm * cNorm; + + double expWeight = ExpoTransWeight::weight(cs, d, t0, d0, rOpts); + + return normWeight + expWeight; +} + +// _____________________________________________________________________________ +uint32_t NormDistrTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + UNUSED(rOpts); + UNUSED(c); + + throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight")); +} + +// _____________________________________________________________________________ + +// _____________________________________________________________________________ +double DistDiffTransWeight::weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(t0); + UNUSED(c); + // double mean = 250; // expectation value of 250 meters for buses + // double lambda = 1.0 / mean; + + double w = fabs(d - d0); + + return rOpts.transitionPen * w; +} + +// _____________________________________________________________________________ +uint32_t DistDiffTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + UNUSED(rOpts); + UNUSED(c); + + throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight")); +} + +// _____________________________________________________________________________ +uint32_t DistDiffTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) { + UNUSED(tTime); + UNUSED(rOpts); + return std::numeric_limits::max(); +} diff --git a/src/pfaedle/router/Weights.h b/src/pfaedle/router/Weights.h new file mode 100644 index 0000000..646f93b --- /dev/null +++ b/src/pfaedle/router/Weights.h @@ -0,0 +1,161 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_WEIGHTS_H_ +#define PFAEDLE_ROUTER_WEIGHTS_H_ + +#include +#include "pfaedle/osm/Restrictor.h" +#include "pfaedle/router/Misc.h" +#include "pfaedle/router/RoutingAttrs.h" +#include "pfaedle/trgraph/Graph.h" +#include "util/graph/EDijkstra.h" + +namespace pfaedle { +namespace router { + +typedef util::graph::EDijkstra::CostFunc + RCostFunc; +typedef util::graph::EDijkstra::HeurFunc + RHeurFunc; + +class ExpoTransWeight { + public: + struct CostFunc : public RCostFunc { + CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& res, uint32_t max) + : _rAttrs(rAttrs), + _rOpts(rOpts), + _res(res), + _inf(max), + _noLineSimiPen(false), + _lastFrom(0) { + if (_rAttrs.lineFrom.empty() && _rAttrs.lineTo.empty() && + _rAttrs.shortName.empty()) { + _noLineSimiPen = true; + } + if (_rOpts.lineUnmatchedPunishFact == 1) { + _noLineSimiPen = true; + } + } + + const RoutingAttrs& _rAttrs; + const RoutingOpts& _rOpts; + const osm::Restrictor& _res; + uint32_t _inf; + bool _noLineSimiPen; + mutable const trgraph::Edge* _lastFrom; + mutable uint32_t _lastC; + + uint32_t operator()(const trgraph::Edge* from, const trgraph::Node* n, + const trgraph::Edge* to) const; + uint32_t inf() const { return _inf; } + + LineSimilarity transitLineSimi(const trgraph::Edge* e) const; + }; + + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos); + + const RoutingOpts& _rOpts; + double _maxV; + POINT _center; + double _maxCentD; + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const; + mutable const trgraph::Edge* _lastE; + mutable uint32_t _lastC; + }; + + static uint32_t maxCost(double tTime, const RoutingOpts& rOpts); + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = true; + static const bool NEED_DIST = false; +}; + +class ExpoTransWeightNoHeur : public ExpoTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +class NormDistrTransWeight : public ExpoTransWeight { + public: + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = false; + static const bool NEED_DIST = false; +}; + +class NormDistrTransWeightNoHeur : public NormDistrTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +class DistDiffTransWeight : public ExpoTransWeight { + public: + static uint32_t maxCost(double tTime, const RoutingOpts& rOpts); + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = false; + static const bool NEED_DIST = true; +}; + +class DistDiffTransWeightNoHeur : public DistDiffTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_WEIGHTS_H_ diff --git a/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp b/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp new file mode 100644 index 0000000..f354311 --- /dev/null +++ b/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp @@ -0,0 +1,30 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include "pfaedle/Def.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" +#include "util/geo/Geo.h" + +using pfaedle::statsimiclassifier::JaccardClassifier; + +// _____________________________________________________________________________ +bool JaccardClassifier::similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, + const POINT& posB) const { + UNUSED(posA); + UNUSED(posB); + return similar(nameA, nameB); +} + +// _____________________________________________________________________________ +bool JaccardClassifier::similar(const std::string& nameA, + const std::string& nameB) const { + // hard similarity + if (nameA == nameB) return true; + + return util::jaccardSimi(nameA, nameB) > 0.45; // 0.45 from paper +} diff --git a/src/pfaedle/statsimi-classifier/StatsimiClassifier.h b/src/pfaedle/statsimi-classifier/StatsimiClassifier.h new file mode 100644 index 0000000..dac508b --- /dev/null +++ b/src/pfaedle/statsimi-classifier/StatsimiClassifier.h @@ -0,0 +1,35 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ +#define PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ + +#include +#include "pfaedle/Def.h" +#include "util/geo/Geo.h" + +namespace pfaedle { +namespace statsimiclassifier { + +class StatsimiClassifier { + public: + virtual bool similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, const POINT& posB) const = 0; + + virtual bool similar(const std::string& nameA, + const std::string& nameB) const = 0; +}; + +class JaccardClassifier : public StatsimiClassifier { + public: + virtual bool similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, const POINT& posB) const; + virtual bool similar(const std::string& nameA, + const std::string& nameB) const; +}; + +} // namespace statsimiclassifier +} // namespace pfaedle + +#endif // PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ diff --git a/src/pfaedle/tests/CMakeLists.txt b/src/pfaedle/tests/CMakeLists.txt new file mode 100644 index 0000000..deb3632 --- /dev/null +++ b/src/pfaedle/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(pfaedleTest TestMain.cpp) +target_link_libraries(pfaedleTest pfaedle_dep util) diff --git a/src/pfaedle/tests/TestMain.cpp b/src/pfaedle/tests/TestMain.cpp new file mode 100644 index 0000000..4187a9b --- /dev/null +++ b/src/pfaedle/tests/TestMain.cpp @@ -0,0 +1,329 @@ +// Copyright 2020 +// Author: Patrick Brosi + +#include "pfaedle/osm/Restrictor.h" + +#define private public +#include "pfaedle/router/Router.h" +#undef private +#define private private + +using pfaedle::osm::Restrictor; +using pfaedle::router::CostMatrix; +using pfaedle::router::EdgeCandGroup; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::LayerCostsDAG; +using pfaedle::router::RouterImpl; +using pfaedle::router::RoutingAttrs; +using pfaedle::router::RoutingOpts; +using util::approx; + +// _____________________________________________________________________________ +uint32_t cmGet(const CostMatrix& m, size_t i, size_t j) { + for (const auto& e : m) { + if (e.first.first == i && e.first.second == j) return e.second; + } + + return -1; +} + +// _____________________________________________________________________________ +int main(int argc, char** argv) { + UNUSED(argc); + UNUSED(argv); + RouterImpl router; + + RoutingAttrs rAttrs; + RoutingOpts rOpts; + Restrictor restr; + LayerCostsDAG initCosts; + + // to make sure we always underestimate the cost in the heuristic for testing + pfaedle::trgraph::NodePL::comps.emplace_back( + pfaedle::trgraph::Component{9999999}); + + // build transit graph + pfaedle::trgraph::Graph g; + auto a = g.addNd(POINT{0, 0}); + auto b = g.addNd(POINT{0, 10}); + auto c = g.addNd(POINT{10, 0}); + auto d = g.addNd(POINT{20, 0}); + + a->pl().setComp(1); + b->pl().setComp(1); + c->pl().setComp(1); + d->pl().setComp(1); + + auto eA = g.addEdg(a, c); + auto eB = g.addEdg(b, c); + auto eC = g.addEdg(c, d); + + eA->pl().setCost(10); + eB->pl().setCost(6); + eC->pl().setCost(100); + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(10)); + TEST(cmGet(costM, 1, 0), ==, approx(6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(50 + 10)); + TEST(cmGet(costM, 1, 0), ==, approx(50 + 6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), ==, approx(2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0.9, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(90 + 5)); + TEST(cmGet(costM, 1, 0), ==, approx(90 + 2)); + } + + // with hopsfast + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(50 + 6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0.9, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(90 + 2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{9999, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), ==, approx(10)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 1, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5 + 100)); + TEST(cmGet(costM, 1, 0), ==, approx(10 + 100)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + + tos.push_back({eC, 0, 1, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5 + 100)); + TEST(cmGet(costM, 1, 0), ==, approx(10 + 100)); + TEST(cmGet(costM, 0, 1), ==, approx(5 + 50)); + TEST(cmGet(costM, 1, 1), ==, approx(10 + 50)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + TEST(cmGet(costM, 2, 1), >=, maxTime); + } + + exit(0); +} diff --git a/src/pfaedle/trgraph/EdgePL.cpp b/src/pfaedle/trgraph/EdgePL.cpp index cd6c241..7c5b613 100644 --- a/src/pfaedle/trgraph/EdgePL.cpp +++ b/src/pfaedle/trgraph/EdgePL.cpp @@ -11,15 +11,12 @@ using pfaedle::trgraph::EdgePL; using pfaedle::trgraph::TransitEdgeLine; - std::map EdgePL::_flines; std::map EdgePL::_tlines; // _____________________________________________________________________________ EdgePL::EdgePL() - : _length(0), _oneWay(0), _hasRestr(false), _rev(false), _lvl(0) { - _l = new LINE(); - _flines[_l] = 1; + : _oneWay(0), _hasRestr(false), _rev(false), _lvl(0), _cost(0), _l(0) { } // _____________________________________________________________________________ @@ -27,17 +24,20 @@ EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {} // _____________________________________________________________________________ EdgePL::EdgePL(const EdgePL& pl, bool geoflat) - : _length(pl._length), - _oneWay(pl._oneWay), + : _oneWay(pl._oneWay), _hasRestr(pl._hasRestr), _rev(pl._rev), - _lvl(pl._lvl) { - if (geoflat) { - _l = pl._l; - } else { - _l = new LINE(*pl._l); + _lvl(pl._lvl), + _cost(pl._cost), + _l(0) { + if (pl._l) { + if (geoflat) { + _l = pl._l; + } else { + _l = new LINE(*pl._l); + } + _flines[_l]++; } - _flines[_l]++; for (auto l : pl._lines) addLine(l); } @@ -75,16 +75,23 @@ EdgePL EdgePL::revCopy() const { } // _____________________________________________________________________________ -void EdgePL::setLength(double d) { _length = d; } +double EdgePL::getLength() const { + double len = 0; -// _____________________________________________________________________________ -double EdgePL::getLength() const { return _length; } + for (size_t i = 1; i < _l->size(); i++) { + len += haversine((*_l)[i-1], (*_l)[i]); + } + + return len; +} // _____________________________________________________________________________ void EdgePL::addLine(const TransitEdgeLine* l) { - if (std::find(_lines.begin(), _lines.end(), l) == _lines.end()) { + auto lb = std::lower_bound(_lines.begin(), _lines.end(), l); + if (lb == _lines.end() || *lb != l) { _lines.reserve(_lines.size() + 1); - _lines.push_back(l); + lb = std::lower_bound(_lines.begin(), _lines.end(), l); + _lines.insert(lb, l); if (_tlines.count(l)) _tlines[l]++; else @@ -103,7 +110,13 @@ const std::vector& EdgePL::getLines() const { } // _____________________________________________________________________________ -void EdgePL::addPoint(const POINT& p) { _l->push_back(p); } +void EdgePL::addPoint(const POINT& p) { + if (!_l) { + _l = new LINE(); + _flines[_l] = 1; + } + _l->push_back(p); +} // _____________________________________________________________________________ const LINE* EdgePL::getGeom() const { return _l; } @@ -114,8 +127,9 @@ LINE* EdgePL::getGeom() { return _l; } // _____________________________________________________________________________ util::json::Dict EdgePL::getAttrs() const { util::json::Dict obj; - obj["m_length"] = std::to_string(_length); + obj["m_length"] = std::to_string(getLength()); obj["oneway"] = std::to_string(static_cast(_oneWay)); + obj["cost"] = std::to_string(static_cast(_cost) / 10.0); obj["level"] = std::to_string(_lvl); obj["restriction"] = isRestricted() ? "yes" : "no"; @@ -152,10 +166,10 @@ void EdgePL::setOneWay(uint8_t dir) { _oneWay = dir; } void EdgePL::setOneWay() { _oneWay = 1; } // _____________________________________________________________________________ -void EdgePL::setLvl(uint8_t lvl) { _lvl = lvl; } +uint32_t EdgePL::getCost() const { return _cost; } // _____________________________________________________________________________ -uint8_t EdgePL::lvl() const { return _lvl; } +void EdgePL::setCost(uint32_t c) { _cost = c; } // _____________________________________________________________________________ void EdgePL::setRev() { _rev = true; } diff --git a/src/pfaedle/trgraph/EdgePL.h b/src/pfaedle/trgraph/EdgePL.h index 368ac2f..3c71fea 100644 --- a/src/pfaedle/trgraph/EdgePL.h +++ b/src/pfaedle/trgraph/EdgePL.h @@ -16,8 +16,6 @@ using util::geograph::GeoEdgePL; - - namespace pfaedle { namespace trgraph { @@ -28,14 +26,17 @@ struct TransitEdgeLine { std::string fromStr; std::string toStr; std::string shortName; + uint32_t color; }; inline bool operator==(const TransitEdgeLine& a, const TransitEdgeLine& b) { + // ignoring color here! return a.fromStr == b.fromStr && a.toStr == b.toStr && a.shortName == b.shortName; } inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) { + // ignoring color here! return a.fromStr < b.fromStr || (a.fromStr == b.fromStr && a.toStr < b.toStr) || (a.fromStr == b.fromStr && a.toStr == b.toStr && @@ -65,14 +66,20 @@ class EdgePL { // Return the length in meters stored for this edge payload double getLength() const; - // Set the length in meters for this edge payload - void setLength(double d); - // Set this edge as a one way node, either in the default direction of // the edge (no arg), or the direction specified in dir void setOneWay(); void setOneWay(uint8_t dir); + void setLvl(uint8_t lvl) { assert(lvl < 9); _lvl = lvl; } + uint8_t lvl() const { return _lvl; } + + // Return the cost for this edge payload + uint32_t getCost() const; + + // Set the cost for this edge payload + void setCost(uint32_t d); + // Mark this payload' edge as having some restrictions void setRestricted(); @@ -85,12 +92,6 @@ class EdgePL { // True if this edge is restricted bool isRestricted() const; - // Set the level of this edge. - void setLvl(uint8_t lvl); - - // Return the level of this edge. - uint8_t lvl() const; - // Return the one-way code stored for this edge. uint8_t oneWay() const; @@ -115,11 +116,11 @@ class EdgePL { EdgePL revCopy() const; private: - float _length; uint8_t _oneWay : 2; bool _hasRestr : 1; bool _rev : 1; - uint8_t _lvl : 3; + uint8_t _lvl: 4; + uint32_t _cost; // costs in 1/10th seconds LINE* _l; diff --git a/src/pfaedle/trgraph/Graph.h b/src/pfaedle/trgraph/Graph.h index 7e3baba..7f7d50a 100644 --- a/src/pfaedle/trgraph/Graph.h +++ b/src/pfaedle/trgraph/Graph.h @@ -24,8 +24,8 @@ namespace trgraph { typedef util::graph::Edge Edge; typedef util::graph::Node Node; typedef util::graph::DirGraph Graph; -typedef Grid NodeGrid; -typedef Grid EdgeGrid; +typedef Grid NodeGrid; +typedef Grid EdgeGrid; } // namespace trgraph } // namespace pfaedle diff --git a/src/pfaedle/trgraph/NodePL.cpp b/src/pfaedle/trgraph/NodePL.cpp index a0aec0a..b4bba8b 100644 --- a/src/pfaedle/trgraph/NodePL.cpp +++ b/src/pfaedle/trgraph/NodePL.cpp @@ -3,22 +3,19 @@ // Authors: Patrick Brosi #include +#include +#include #include #include "pfaedle/trgraph/NodePL.h" -#include "pfaedle/trgraph/StatGroup.h" #include "pfaedle/trgraph/StatInfo.h" #include "util/String.h" -using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::NodePL; using pfaedle::trgraph::Component; +using pfaedle::trgraph::NodePL; +using pfaedle::trgraph::StatInfo; -// we use the adress of this dummy station info as a special value -// of this node, meaning "is a station block". Re-using the _si field here -// saves some memory -StatInfo NodePL::_blockerSI = StatInfo(); - -std::unordered_map NodePL::_comps; +std::vector NodePL::comps; +std::vector NodePL::_statInfos; // _____________________________________________________________________________ NodePL::NodePL() @@ -32,19 +29,6 @@ NodePL::NodePL() { } -// _____________________________________________________________________________ -NodePL::NodePL(const NodePL& pl) - : _geom(pl._geom), - _si(0), - _component(pl._component) -#ifdef PFAEDLE_DBG - , - _vis(pl._vis) -#endif -{ - if (pl._si) setSI(*(pl._si)); -} - // _____________________________________________________________________________ NodePL::NodePL(const POINT& geom) : _geom(geom), @@ -70,18 +54,6 @@ NodePL::NodePL(const POINT& geom, const StatInfo& si) setSI(si); } -// _____________________________________________________________________________ -NodePL::~NodePL() { - if (getSI()) delete _si; - if (_component) { - _comps[_component]--; - if (_comps[_component] == 0) { - delete _component; - _comps.erase(_comps.find(_component)); - } - } -} - // _____________________________________________________________________________ void NodePL::setVisited() const { #ifdef PFAEDLE_DBG @@ -93,18 +65,14 @@ void NodePL::setVisited() const { void NodePL::setNoStat() { _si = 0; } // _____________________________________________________________________________ -const Component* NodePL::getComp() const { return _component; } +const Component& NodePL::getComp() const { return comps[_component - 1]; } // _____________________________________________________________________________ -void NodePL::setComp(const Component* c) { - if (_component == c) return; - _component = c; +uint32_t NodePL::getCompId() const { return _component; } - // NOT thread safe! - if (!_comps.count(c)) - _comps[c] = 1; - else - _comps[c]++; +// _____________________________________________________________________________ +void NodePL::setComp(uint32_t id) { + _component = id; } // _____________________________________________________________________________ @@ -116,54 +84,59 @@ void NodePL::setGeom(const POINT& geom) { _geom = geom; } // _____________________________________________________________________________ util::json::Dict NodePL::getAttrs() const { util::json::Dict obj; - obj["component"] = std::to_string(reinterpret_cast(_component)); + obj["component"] = std::to_string(_component); #ifdef PFAEDLE_DBG obj["dijkstra_vis"] = _vis ? "yes" : "no"; #endif if (getSI()) { obj["station_info_ptr"] = util::toString(_si); - obj["station_name"] = _si->getName(); - obj["station_alt_names"] = util::implode(_si->getAltNames(), ","); - obj["from_osm"] = _si->isFromOsm() ? "yes" : "no"; - obj["station_platform"] = _si->getTrack(); - obj["station_group"] = - std::to_string(reinterpret_cast(_si->getGroup())); + obj["station_name"] = getSI()->getName(); + obj["station_alt_names"] = + util::implode(getSI()->getAltNames(), ","); + obj["station_platform"] = getSI()->getTrack(); #ifdef PFAEDLE_STATION_IDS // only print this in debug mode - obj["station_id"] = _si->getId(); + obj["station_id"] = getSI()->getId(); #endif - - - std::stringstream gtfsIds; - if (_si->getGroup()) { - for (auto* s : _si->getGroup()->getStops()) { - gtfsIds << s->getId() << " (" << s->getName() << "),"; - } - } - - obj["station_group_stops"] = gtfsIds.str(); } return obj; } // _____________________________________________________________________________ -void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); } +void NodePL::setSI(const StatInfo& si) { + _statInfos.emplace_back(si); + _si = _statInfos.size(); +} // _____________________________________________________________________________ const StatInfo* NodePL::getSI() const { if (isBlocker()) return 0; - return _si; + if (isTurnCycle()) return 0; + if (_si == 0) return 0; + return &_statInfos[_si - 1]; } // _____________________________________________________________________________ StatInfo* NodePL::getSI() { if (isBlocker()) return 0; - return _si; + if (isTurnCycle()) return 0; + if (_si == 0) return 0; + return &_statInfos[_si - 1]; } // _____________________________________________________________________________ -void NodePL::setBlocker() { _si = &_blockerSI; } +void NodePL::setTurnCycle() { _si = std::numeric_limits::max() - 1; } // _____________________________________________________________________________ -bool NodePL::isBlocker() const { return _si == &_blockerSI; } +bool NodePL::isTurnCycle() const { + return _si == (std::numeric_limits::max() - 1); +} + +// _____________________________________________________________________________ +void NodePL::setBlocker() { _si = std::numeric_limits::max(); } + +// _____________________________________________________________________________ +bool NodePL::isBlocker() const { + return _si == std::numeric_limits::max(); +} diff --git a/src/pfaedle/trgraph/NodePL.h b/src/pfaedle/trgraph/NodePL.h index 7c1cdb0..8d4efa9 100644 --- a/src/pfaedle/trgraph/NodePL.h +++ b/src/pfaedle/trgraph/NodePL.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "ad/cppgtfs/gtfs/Feed.h" #include "pfaedle/Def.h" #include "pfaedle/trgraph/StatInfo.h" @@ -20,7 +21,7 @@ namespace pfaedle { namespace trgraph { struct Component { - uint8_t minEdgeLvl : 3; + float maxSpeed; }; /* @@ -29,10 +30,8 @@ struct Component { class NodePL { public: NodePL(); - NodePL(const NodePL& pl); // NOLINT NodePL(const POINT& geom); // NOLINT NodePL(const POINT& geom, const StatInfo& si); - ~NodePL(); // Return the geometry of this node. const POINT* getGeom() const; @@ -52,10 +51,13 @@ class NodePL { void setNoStat(); // Get the component of this node - const Component* getComp() const; + const Component& getComp() const; + + // Get the component of this node + uint32_t getCompId() const; // Set the component of this node - void setComp(const Component* c); + void setComp(uint32_t c); // Make this node a blocker void setBlocker(); @@ -63,21 +65,27 @@ class NodePL { // Check if this node is a blocker bool isBlocker() const; + // Make this node a turning cycle + void setTurnCycle(); + + // Check if this node is a blocker + bool isTurnCycle() const; + // Mark this node as visited (usefull for counting search space in Dijkstra) // (only works for DEBUG build type) void setVisited() const; + static std::vector comps; + private: POINT _geom; - StatInfo* _si; - const Component* _component; + uint32_t _si; + uint32_t _component; #ifdef PFAEDLE_DBG mutable bool _vis; #endif - - static StatInfo _blockerSI; - static std::unordered_map _comps; + static std::vector _statInfos; }; } // namespace trgraph } // namespace pfaedle diff --git a/src/pfaedle/trgraph/Normalizer.cpp b/src/pfaedle/trgraph/Normalizer.cpp index 5e254a0..5daaa80 100644 --- a/src/pfaedle/trgraph/Normalizer.cpp +++ b/src/pfaedle/trgraph/Normalizer.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -37,17 +36,6 @@ Normalizer& Normalizer::operator=(Normalizer other) { return *this; } -// _____________________________________________________________________________ -std::string Normalizer::operator()(std::string sn) const { - return normTS(sn); -} - -// _____________________________________________________________________________ -std::string Normalizer::normTS(const std::string& sn) const { - std::lock_guard lock(_mutex); - return norm(sn); -} - // _____________________________________________________________________________ std::string Normalizer::norm(const std::string& sn) const { auto i = _cache.find(sn); diff --git a/src/pfaedle/trgraph/Normalizer.h b/src/pfaedle/trgraph/Normalizer.h index 6546963..ae02d5a 100644 --- a/src/pfaedle/trgraph/Normalizer.h +++ b/src/pfaedle/trgraph/Normalizer.h @@ -10,7 +10,6 @@ #include #include #include -#include namespace pfaedle { namespace trgraph { @@ -37,19 +36,13 @@ class Normalizer { // Normalize sn, not thread safe std::string norm(const std::string& sn) const; - // Normalize sn, thread safe - std::string normTS(const std::string& sn) const; - // Normalize sn based on the rules of this normalizer, uses the thread safe - // version of norm() internally - std::string operator()(std::string sn) const; bool operator==(const Normalizer& b) const; private: ReplRulesComp _rules; ReplRules _rulesOrig; mutable std::unordered_map _cache; - mutable std::mutex _mutex; void buildRules(const ReplRules& rules); }; diff --git a/src/pfaedle/trgraph/StatGroup.cpp b/src/pfaedle/trgraph/StatGroup.cpp deleted file mode 100644 index 75b45d4..0000000 --- a/src/pfaedle/trgraph/StatGroup.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include -#include "pfaedle/trgraph/StatGroup.h" -#include "util/geo/Geo.h" - -using pfaedle::trgraph::StatGroup; -using pfaedle::trgraph::Node; -using pfaedle::router::NodeCandGroup; -using ad::cppgtfs::gtfs::Stop; - -// _____________________________________________________________________________ -StatGroup::StatGroup() {} - -// _____________________________________________________________________________ -void StatGroup::addStop(const Stop* s) { _stops.insert(s); } - -// _____________________________________________________________________________ -void StatGroup::addNode(trgraph::Node* n) { _nodes.insert(n); } - -// _____________________________________________________________________________ -void StatGroup::merge(StatGroup* other) { - if (other == this) return; - - std::set nds = other->getNodes(); - std::set stops = other->getStops(); - - for (auto on : nds) { - on->pl().getSI()->setGroup(this); - addNode(on); - } - - for (auto* os : stops) { - addStop(os); - } -} - -// _____________________________________________________________________________ -const NodeCandGroup& StatGroup::getNodeCands(const Stop* s) const { - return _stopNodePens.at(s); -} - -// _____________________________________________________________________________ -const std::set& StatGroup::getNodes() const { return _nodes; } - -// _____________________________________________________________________________ -void StatGroup::remNode(trgraph::Node* n) { - auto it = _nodes.find(n); - if (it != _nodes.end()) _nodes.erase(it); -} - -// _____________________________________________________________________________ -std::set& StatGroup::getNodes() { return _nodes; } - -// _____________________________________________________________________________ -const std::set& StatGroup::getStops() const { return _stops; } - -// _____________________________________________________________________________ -double StatGroup::getPen(const Stop* s, trgraph::Node* n, - const trgraph::Normalizer& platformNorm, - double trackPen, double distPenFac, - double nonOsmPen) const { - POINT p = - util::geo::latLngToWebMerc(s->getLat(), s->getLng()); - - double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom()); - distPen *= distPenFac; - - std::string platform = platformNorm.norm(s->getPlatformCode()); - - if (!platform.empty() && !n->pl().getSI()->getTrack().empty() && - n->pl().getSI()->getTrack() == platform) { - trackPen = 0; - } - - if (n->pl().getSI()->isFromOsm()) nonOsmPen = 0; - - return distPen + trackPen + nonOsmPen; -} - -// _____________________________________________________________________________ -void StatGroup::writePens(const trgraph::Normalizer& platformNorm, - double trackPen, double distPenFac, - double nonOsmPen) { - if (_stopNodePens.size()) return; // already written - for (auto* s : _stops) { - for (auto* n : _nodes) { - _stopNodePens[s].push_back(router::NodeCand{ - n, getPen(s, n, platformNorm, trackPen, distPenFac, nonOsmPen)}); - } - } -} diff --git a/src/pfaedle/trgraph/StatGroup.h b/src/pfaedle/trgraph/StatGroup.h deleted file mode 100644 index a3341af..0000000 --- a/src/pfaedle/trgraph/StatGroup.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_ -#define PFAEDLE_TRGRAPH_STATGROUP_H_ - -#include -#include -#include -#include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/router/Router.h" -#include "pfaedle/trgraph/Graph.h" -#include "pfaedle/trgraph/Normalizer.h" - -namespace pfaedle { -namespace trgraph { - -using ad::cppgtfs::gtfs::Stop; - -/* - * A group of stations that belong together semantically (for example, multiple - * stop points of a larger bus station) - */ -class StatGroup { - public: - StatGroup(); - StatGroup(const StatGroup& a) = delete; - - // Add a stop s to this station group - void addStop(const Stop* s); - - // Add a node n to this station group - void addNode(trgraph::Node* n); - - // Return all nodes contained in this group - const std::set& getNodes() const; - std::set& getNodes(); - - // Return all stops contained in this group - const std::set& getStops() const; - - // Remove a node from this group - void remNode(trgraph::Node* n); - - // All nodes in other will be in this group, their SI's updated, and the - // "other" group deleted. - void merge(StatGroup* other); - - // Return node candidates for stop s from this group - const router::NodeCandGroup& getNodeCands(const Stop* s) const; - - // Write the penalties for all stops contained in this group so far. - void writePens(const trgraph::Normalizer& platformNorm, double trackPen, - double distPenFac, double nonOsmPen); - - private: - std::set _nodes; - std::set _stops; - - // for each stop in this group, a penalty for each of the nodes here, based on - // its distance and optionally the track number - std::unordered_map _stopNodePens; - - double getPen(const Stop* s, trgraph::Node* n, - const trgraph::Normalizer& norm, double trackPen, - double distPenFac, double nonOsmPen) const; -}; -} // namespace trgraph -} // namespace pfaedle - -#endif // PFAEDLE_TRGRAPH_STATGROUP_H_ diff --git a/src/pfaedle/trgraph/StatInfo.cpp b/src/pfaedle/trgraph/StatInfo.cpp index 0b642b6..132c985 100644 --- a/src/pfaedle/trgraph/StatInfo.cpp +++ b/src/pfaedle/trgraph/StatInfo.cpp @@ -3,66 +3,24 @@ // Authors: Patrick Brosi #include "pfaedle/router/Comp.h" -#include "pfaedle/trgraph/StatGroup.h" #include "pfaedle/trgraph/StatInfo.h" using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::StatGroup; - -std::unordered_map StatInfo::_groups; // _____________________________________________________________________________ -StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {} +StatInfo::StatInfo() : _name(""), _track("") {} // _____________________________________________________________________________ StatInfo::StatInfo(const StatInfo& si) - : _name(si._name), - _altNames(si._altNames), - _track(si._track), - _fromOsm(si._fromOsm), - _group(0) { - setGroup(si._group); + : _name(si._name), _altNames(si._altNames), _track(si._track) { #ifdef PFAEDLE_STATION_IDS _id = si._id; #endif } // _____________________________________________________________________________ -StatInfo::StatInfo(const std::string& name, const std::string& track, - bool fromOsm) - : _name(name), _track(track), _fromOsm(fromOsm), _group(0) {} - -// _____________________________________________________________________________ -StatInfo::~StatInfo() { unRefGroup(_group); } - -// _____________________________________________________________________________ -void StatInfo::unRefGroup(StatGroup* g) { - if (g) { - _groups[g]--; - if (_groups[g] == 0) { - // std::cout << "Deleting " << g << std::endl; - delete g; - _groups.erase(_groups.find(g)); - } - } -} - -// _____________________________________________________________________________ -void StatInfo::setGroup(StatGroup* g) { - if (_group == g) return; - unRefGroup(_group); - - _group = g; - - // NOT thread safe! - if (!_groups.count(g)) - _groups[g] = 1; - else - _groups[g]++; -} - -// _____________________________________________________________________________ -StatGroup* StatInfo::getGroup() const { return _group; } +StatInfo::StatInfo(const std::string& name, const std::string& track) + : _name(name), _track(track) {} // _____________________________________________________________________________ const std::string& StatInfo::getName() const { return _name; } @@ -70,12 +28,6 @@ const std::string& StatInfo::getName() const { return _name; } // _____________________________________________________________________________ const std::string& StatInfo::getTrack() const { return _track; } -// _____________________________________________________________________________ -bool StatInfo::isFromOsm() const { return _fromOsm; } - -// _____________________________________________________________________________ -void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; } - // _____________________________________________________________________________ double StatInfo::simi(const StatInfo* other) const { if (!other) return 0; diff --git a/src/pfaedle/trgraph/StatInfo.h b/src/pfaedle/trgraph/StatInfo.h index de0bbcf..dcd5ba7 100644 --- a/src/pfaedle/trgraph/StatInfo.h +++ b/src/pfaedle/trgraph/StatInfo.h @@ -6,24 +6,20 @@ #define PFAEDLE_TRGRAPH_STATINFO_H_ #include -#include #include +#include namespace pfaedle { namespace trgraph { -// forward declaration -class StatGroup; - /* - * Meta information (name, alternative names, track, group...) of a single stop + * Meta information (name, alternative names, track, ...) of a single stop */ class StatInfo { public: StatInfo(); StatInfo(const StatInfo& si); - StatInfo(const std::string& name, const std::string& track, bool _fromOsm); - ~StatInfo(); + StatInfo(const std::string& name, const std::string& track); // Return this stops names. const std::string& getName() const; @@ -43,18 +39,6 @@ class StatInfo { // Return the similarity between this stop and other double simi(const StatInfo* other) const; - // Set this stations group. - void setGroup(StatGroup* g); - - // Return this stations group. - StatGroup* getGroup() const; - - // True if this stop was from osm - bool isFromOsm() const; - - // Set this stop as coming from osm - void setIsFromOsm(bool is); - #ifdef PFAEDLE_STATION_IDS const std::string& getId() const { return _id; } void setId(const std::string& id) { _id = id; } @@ -64,17 +48,12 @@ class StatInfo { std::string _name; std::vector _altNames; std::string _track; - bool _fromOsm; - StatGroup* _group; #ifdef PFAEDLE_STATION_IDS // debug feature to store station ids from both OSM // and GTFS std::string _id; #endif - - static std::unordered_map _groups; - static void unRefGroup(StatGroup* g); }; } // namespace trgraph } // namespace pfaedle diff --git a/src/shapevl/CMakeLists.txt b/src/shapevl/CMakeLists.txt new file mode 100644 index 0000000..e6bdc17 --- /dev/null +++ b/src/shapevl/CMakeLists.txt @@ -0,0 +1,15 @@ +file(GLOB_RECURSE shapevl_SRC *.cpp) + +set(shapevl_main ShapevlMain.cpp) + +list(REMOVE_ITEM shapevl_SRC ${shapevl_main}) + +include_directories( + ${PFAEDLE_INCLUDE_DIR} +) + +add_executable(shapevl ${shapevl_main}) +add_library(shapevl_dep ${shapevl_SRC}) + +include_directories(shapevl_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) +target_link_libraries(shapevl shapevl_dep util ad_cppgtfs -lpthread) diff --git a/src/shapevl/Collector.cpp b/src/shapevl/Collector.cpp new file mode 100644 index 0000000..610192d --- /dev/null +++ b/src/shapevl/Collector.cpp @@ -0,0 +1,373 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/Def.h" +#include "shapevl/Collector.h" +#include "shapevl/Result.h" +#include "util/geo/Geo.h" +#include "util/geo/PolyLine.h" +#include "util/geo/output/GeoJsonOutput.h" +#include "util/log/Log.h" + +using util::geo::PolyLine; + +using ad::cppgtfs::gtfs::Shape; +using ad::cppgtfs::gtfs::Trip; +using pfaedle::eval::Collector; +using pfaedle::eval::Result; +using util::geo::output::GeoJsonOutput; + +// _____________________________________________________________________________ +double Collector::add(const Trip* oldT, const Shape* oldS, const Trip* newT, + const Shape* newS) { + // This adds a new trip with a new shape to our evaluation. + + _trips++; + + if (!oldS) { + // If there is no original shape, we cannot compare them - abort! + _noOrigShp++; + return 0; + } + + for (auto st : oldT->getStopTimes()) { + if (st.getShapeDistanceTravelled() < 0) { + // we cannot safely compare trips without shape dist travelled + // information - abort! + _noOrigShp++; + return 0; + } + } + + for (auto st : newT->getStopTimes()) { + if (st.getShapeDistanceTravelled() < 0) { + // we cannot safely compare trips without shape dist travelled + // information - abort! + _noOrigShp++; + return 0; + } + } + + double fd = 0; + + // A "segment" is a path from station s_i to station s_{i+1} + + size_t unmatchedSegments; // number of unmatched segments + double unmatchedSegmentsLength; // total _acc. length of unmatched segments + + std::vector oldDists; + LINE oldL = getWebMercLine(oldS, &oldDists); + + std::vector newDists; + LINE newL = getWebMercLine(newS, &newDists); + + auto oldSegs = segmentize(oldT, oldL, oldDists); + auto newSegs = segmentize(newT, newL, newDists); + + // new lines build from cleaned-up shapes + LINE oldLCut; + LINE newLCut; + + for (auto oldL : oldSegs) + oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); + + for (auto newL : newSegs) + newLCut.insert(newLCut.end(), newL.begin(), newL.end()); + + // determine the scale factor between the distance in projected + // coordinates and the real-world distance in meters + auto avgY = + (oldSegs.front().front().getY() + oldSegs.back().back().getY()) / 2; + double fac = cos(2 * atan(exp(avgY / 6378137.0)) - 1.5707965); + + double SEGL = 10; + + if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS->getId())) { + fd = _dCache[oldS][newS->getId()]; + } else { + fd = util::geo::accFrechetDistC(oldLCut, newLCut, SEGL / fac) * fac; + _dCache[oldS][newS->getId()] = fd; + } + + 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->getId()] = dA; + unmatchedSegments = dA.first; + unmatchedSegmentsLength = dA.second; + } + + double totL = 0; + for (auto l : oldSegs) totL += util::geo::len(l) * fac; + + // filter out shapes with a length of under 5 meters - they are most likely + // artifacts + if (totL < 5) { + _noOrigShp++; + return 0; + } + + _fdSum += fd / totL; + _unmatchedSegSum += unmatchedSegments; + _unmatchedSegLengthSum += unmatchedSegmentsLength; + + double avgFd = fd / totL; + double AN = static_cast(unmatchedSegments) / + static_cast(oldSegs.size()); + double AL = unmatchedSegmentsLength / totL; + + _results.insert(Result(oldT, avgFd)); + + if (AN <= 0.0001) _acc0++; + if (AN <= 0.1) _acc10++; + if (AN <= 0.2) _acc20++; + if (AN <= 0.4) _acc40++; + if (AN <= 0.8) _acc80++; + + LOG(VDEBUG) << "This result (" << oldT->getId() + << "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size() + << " = " << AN << " A_L/L = " << unmatchedSegmentsLength << "/" + << totL << " = " << AL << " d_f = " << avgFd; + + if (_reportOut) { + (*_reportOut) << oldT->getId() << "\t" << AN << "\t" << AL << "\t" << avgFd + << "\t" << util::geo::getWKT(oldSegs) << "\t" + << util::geo::getWKT(newSegs) << "\n"; + } + + return avgFd; +} + +// _____________________________________________________________________________ +std::vector Collector::segmentize(const Trip* t, const LINE& shape, + const std::vector& dists) { + // The straightforward way to segmentize the shape would be to just cut it at + // the exact measurements in stop_times.txt. We have tried that, but found + // that it produces misleading results for the following reason: + // + // 1) The measurement specifies an exact position on the shape. + // 2) Even if we consider correct rail or bus tracks, the "right" position + // where a vehicle may come to a halt is not a point - its a line segment, + // basically the entire track in railroad term + // 3) The position point on the shape in real-world feeds may be either a) the + // position where a train comes to a halt, b) the position where a carriage + // comes to a halt, c) the beginning of the tracks line segment, d) the end + // of the tracks line segment, e) the center of the tracks line segment, f) + // ... (any position on the tracks line segment. + // 4) The "correct" position is NOT well-defined. + // 5) As tracks are often longer than 20 meters, this will dillute our AN + // measure, although the shape is CORRECT (because the ground truth uses + // a different position philosophy than the test data) + // 6) To normalize this, we always the following approach: + // a) Get the exact progression of the measurment on the shape + // b) Extract a segment of 200 meters, with the measurement progress in the middle + // c) Project the GROUND TRUTH station coordinate to this segment + // d) The result is the cutting point + // 7) If a completely wrong track was chosen, the frechet distance will still + // be greater than 20 meters and AN will measure an unmatch. + // 8) TODO: implement this, explain this in diss + std::vector ret; + + if (t->getStopTimes().size() < 2) return ret; + + POLYLINE pl(shape); + std::vector > cuts; + + size_t i = 0; + for (auto st : t->getStopTimes()) { + cuts.push_back(std::pair( + util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()), + st.getShapeDistanceTravelled())); + i++; + } + + // get first half of geometry, and search for start point there! + size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) - + dists.begin(); + 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); + + for (size_t i = 1; i < cuts.size(); i++) { + size_t before = shape.size(); + if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) { + before = + std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) - + dists.begin(); + } + + POLYLINE beforePl(LINE(shape.begin(), shape.begin() + before)); + + auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex); + + ret.push_back(pl.getSegment(lastLp, curLp).getLine()); + lastLp = curLp; + } + + return ret; +} + +// _____________________________________________________________________________ +LINE Collector::getWebMercLine(const Shape* s, std::vector* dists) { + LINE ret; + + for (size_t i = 0; i < s->getPoints().size(); i++) { + ret.push_back(util::geo::latLngToWebMerc(s->getPoints()[i].lat, + s->getPoints()[i].lng)); + (*dists).push_back(s->getPoints()[i].travelDist); + } + + return ret; +} + +// _____________________________________________________________________________ +const std::set& Collector::getResults() const { return _results; } + +// _____________________________________________________________________________ +double Collector::getAvgDist() const { return _fdSum / _results.size(); } + +// _____________________________________________________________________________ +std::vector Collector::getBins(double mind, double maxd, size_t steps) { + double bin = (maxd - mind) / steps; + double curE = mind + bin; + + std::vector ret; + while (curE <= maxd) { + ret.push_back(curE); + curE += bin; + } + return ret; +} + +// _____________________________________________________________________________ +void Collector::printCsv(std::ostream* os, + const std::set& result) const { + for (auto r : result) (*os) << r.getDist() << "\n"; +} + +// _____________________________________________________________________________ +double Collector::getAcc() const { + return static_cast(_acc0) / static_cast(_results.size()); +} + +// _____________________________________________________________________________ +void Collector::printShortStats(std::ostream* os) const { + if (_results.size()) { + (*os) << "acc-0: " + << (static_cast(_acc0) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-10: " + << (static_cast(_acc10) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-20: " + << (static_cast(_acc20) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-40: " + << (static_cast(_acc40) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-80: " + << (static_cast(_acc80) / + static_cast(_results.size())) * + 100 + << " %"; + } +} + +// _____________________________________________________________________________ +void Collector::printStats(std::ostream* os) const { + (*os) << std::setfill(' ') << std::setw(50) << " # of trips: " << _trips + << "\n"; + (*os) << std::setfill(' ') << std::setw(50) + << " # of trips new shapes were matched for: " << _results.size() + << "\n"; + (*os) << std::setw(50) << " # of trips without input shapes: " << _noOrigShp + << "\n"; + + if (_results.size()) { + (*os) << std::setw(50) << " highest avg frechet distance to input shapes: " + << (--_results.end())->getDist() << " (on trip #" + << (--_results.end())->getTrip()->getId() << ")\n"; + (*os) << std::setw(50) << " lowest distance to input shapes: " + << (_results.begin())->getDist() << " (on trip #" + << (_results.begin())->getTrip()->getId() << ")\n"; + (*os) << std::setw(50) + << " averaged avg frechet distance: " << getAvgDist() << "\n"; + + (*os) << "\n"; + (*os) << " acc-0: " + << (static_cast(_acc0) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-10: " + << (static_cast(_acc10) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-20: " + << (static_cast(_acc20) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-40: " + << (static_cast(_acc40) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-80: " + << (static_cast(_acc80) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + } + + (*os) << std::endl; +} + +// _____________________________________________________________________________ +std::pair Collector::getDa(const std::vector& a, + const std::vector& b) { + assert(a.size() == b.size()); + std::pair ret{0, 0}; + + // euclidean distance on web mercator is in meters on equator, + // and proportional to cos(lat) in both y directions + double fac = + cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) / + 6378137.0)) - + 1.5707965); + + for (size_t i = 0; i < a.size(); i++) { + double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac; + if (fd >= 20) { + ret.first++; + ret.second += util::geo::len(a[i]) * fac; + } + } + + return ret; +} diff --git a/src/pfaedle/eval/Collector.h b/src/shapevl/Collector.h similarity index 64% rename from src/pfaedle/eval/Collector.h rename to src/shapevl/Collector.h index 853314e..876ccbf 100644 --- a/src/pfaedle/eval/Collector.h +++ b/src/shapevl/Collector.h @@ -5,6 +5,7 @@ #ifndef PFAEDLE_EVAL_COLLECTOR_H_ #define PFAEDLE_EVAL_COLLECTOR_H_ +#include #include #include #include @@ -12,13 +13,12 @@ #include #include #include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/gtfs/Feed.h" #include "pfaedle/Def.h" -#include "pfaedle/eval/Result.h" +#include "shapevl/Result.h" #include "util/geo/Geo.h" -using pfaedle::gtfs::Trip; using ad::cppgtfs::gtfs::Shape; +using ad::cppgtfs::gtfs::Trip; namespace pfaedle { namespace eval { @@ -28,18 +28,23 @@ namespace eval { */ class Collector { public: - Collector(const std::string& evalOutPath, const std::vector& dfBins) - : _noOrigShp(0), + Collector(std::ostream* reportOut) + : _trips(0), + _noOrigShp(0), _fdSum(0), _unmatchedSegSum(0), _unmatchedSegLengthSum(0), - _evalOutPath(evalOutPath), - _dfBins(dfBins) {} + _acc0(0), + _acc10(0), + _acc20(0), + _acc40(0), + _acc80(0), + _reportOut(reportOut) {} // Add a shape found by our tool newS for a trip t with newly calculated // station dist values with the old shape oldS - double add(const Trip* t, const Shape* oldS, const Shape& newS, - const std::vector& newDists); + double add(const Trip* oldT, const Shape* oldS, const Trip* newT, + const Shape* newS); // Return the set of all Result objects const std::set& getResults() const; @@ -47,44 +52,44 @@ class Collector { // Print general stats to os void printStats(std::ostream* os) const; - // Print histogramgs for the results to os - void printHisto(std::ostream* os, const std::set& result, - const std::vector& bins) const; + // Print general stats to os + void printShortStats(std::ostream* os) const; // Print a CSV for the results to os - void printCsv(std::ostream* os, const std::set& result, - const std::vector& bins) const; + void printCsv(std::ostream* os, const std::set& result) const; // Return the averaged average frechet distance double getAvgDist() const; - static LINE getWebMercLine(const Shape* s, double from, double to); - static LINE getWebMercLine(const Shape* s, double from, double to, - std::vector* dists); + static LINE getWebMercLine(const Shape* s, std::vector* dists); + + double getAcc() const; private: std::set _results; - std::set _resultsAN; - std::set _resultsAL; std::map > _dCache; std::map > > _dACache; + size_t _trips; size_t _noOrigShp; double _fdSum; size_t _unmatchedSegSum; double _unmatchedSegLengthSum; - std::string _evalOutPath; + size_t _acc0; + size_t _acc10; + size_t _acc20; + size_t _acc40; + size_t _acc80; - std::vector _dfBins; + std::ostream* _reportOut; static std::pair getDa(const std::vector& a, const std::vector& b); static std::vector segmentize(const Trip* t, const LINE& shape, - const std::vector& dists, - const std::vector* newTripDists); + const std::vector& dists); static std::vector getBins(double mind, double maxd, size_t steps); }; diff --git a/src/pfaedle/eval/Result.h b/src/shapevl/Result.h similarity index 93% rename from src/pfaedle/eval/Result.h rename to src/shapevl/Result.h index b039234..0bfa667 100644 --- a/src/pfaedle/eval/Result.h +++ b/src/shapevl/Result.h @@ -5,10 +5,9 @@ #ifndef PFAEDLE_EVAL_RESULT_H_ #define PFAEDLE_EVAL_RESULT_H_ -#include "pfaedle/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h" -using pfaedle::gtfs::Trip; +using ad::cppgtfs::gtfs::Trip; using ad::cppgtfs::gtfs::Shape; namespace pfaedle { diff --git a/src/shapevl/ShapevlMain.cpp b/src/shapevl/ShapevlMain.cpp new file mode 100644 index 0000000..3b94342 --- /dev/null +++ b/src/shapevl/ShapevlMain.cpp @@ -0,0 +1,174 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include +#include +#include +#include +#include "ad/cppgtfs/Parser.h" +#include "shapevl/Collector.h" +#include "util/Misc.h" +#include "util/log/Log.h" + +std::atomic count(0); + +// _____________________________________________________________________________ +void printHelp(int argc, char** argv) { + UNUSED(argc); + std::cout << "Usage: " << argv[0] + << " [-f ] -g [-s] " + << "\n"; + std::cout + << "\nAllowed arguments:\n -g Ground truth GTFS file\n"; + std::cout << " -s Only output summary\n"; + std::cout << " -f Output full reports (per feed) to \n"; + std::cout + << " -m MOTs to match (GTFS MOT or string, default: all)\n"; +} + +// _____________________________________________________________________________ +void eval(const std::vector* paths, + std::vector* colls, + const std::set* mots, + const ad::cppgtfs::gtfs::Feed* evalFeed) { + while (1) { + int myFeed = count-- - 1; + if (myFeed < 0) return; + std::string path = (*paths)[myFeed]; + LOG(DEBUG) << "Reading eval feed " << path << " " + << " ..."; + ad::cppgtfs::gtfs::Feed feed; + ad::cppgtfs::Parser p; + + try { + p.parse(&feed, path); + } catch (const ad::cppgtfs::ParserException& ex) { + LOG(ERROR) << "Could not parse GTFS feed " << path << ", reason was:"; + std::cerr << ex.what() << std::endl; + exit(1); + } + + LOG(DEBUG) << "Evaluating " << path << "..."; + for (const auto oldTrip : evalFeed->getTrips()) { + if (!mots->count(oldTrip.second->getRoute()->getType())) continue; + auto newTrip = feed.getTrips().get(oldTrip.first); + if (!newTrip) + LOG(ERROR) << "Trip #" << oldTrip.first << " not present in " << path; + (*colls)[myFeed].add(oldTrip.second, oldTrip.second->getShape(), newTrip, + newTrip->getShape()); + } + } +} + +// _____________________________________________________________________________ +int main(int argc, char** argv) { + // disable output buffering for standard output + setbuf(stdout, NULL); + + // initialize randomness + srand(time(NULL) + rand()); // NOLINT + + std::string groundTruthFeedPath, motStr; + motStr = "all"; + ad::cppgtfs::gtfs::Feed groundTruthFeed; + std::string fullReportPath = ""; + std::vector evlFeedPaths; + std::set evlFeedPathsUniq; + std::vector evalColls; + std::vector reportStreams; + bool summarize = false; + + for (int i = 1; i < argc; i++) { + std::string cur = argv[i]; + if (cur == "-h" || cur == "--help") { + printHelp(argc, argv); + exit(0); + } else if (cur == "-g") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for ground truth (-g)."; + exit(1); + } + groundTruthFeedPath = argv[i]; + } else if (cur == "-s") { + summarize = true; + } else if (cur == "-f") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for full reports (-f)."; + exit(1); + } + fullReportPath = argv[i]; + } else if (cur == "-m") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for mot (-m)."; + exit(1); + } + motStr = argv[i]; + } else { + char fullPath[PATH_MAX + 1]; + if (!realpath(cur.c_str(), fullPath)) { + LOG(ERROR) << "Error while reading " << fullPath; + exit(1); + } + evlFeedPathsUniq.insert(fullPath); + } + } + + for (const auto& feedPath : evlFeedPathsUniq) { + evlFeedPaths.push_back(feedPath); + if (fullReportPath.size()) { + reportStreams.emplace_back(); + reportStreams.back().open(fullReportPath + "/" + + util::split(feedPath, '/').back() + + ".fullreport.tsv"); + evalColls.push_back({&reportStreams.back()}); + } else { + evalColls.push_back({0}); + } + count++; + } + + if (groundTruthFeedPath.size() == 0) { + LOG(ERROR) << "No ground truth feed path given (-g)."; + exit(1); + } + + std::set mots = + ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr)); + + std::vector evlFeeds(evlFeedPaths.size()); + + try { + LOG(DEBUG) << "Reading ground truth feed" << groundTruthFeedPath << " ..."; + ad::cppgtfs::Parser p; + p.parse(&groundTruthFeed, groundTruthFeedPath); + } catch (const ad::cppgtfs::ParserException& ex) { + LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; + std::cerr << ex.what() << std::endl; + exit(1); + } + + size_t THREADS = std::thread::hardware_concurrency(); + + std::vector thrds(THREADS); + for (auto& thr : thrds) + thr = + std::thread(&eval, &evlFeedPaths, &evalColls, &mots, &groundTruthFeed); + + for (auto& thr : thrds) thr.join(); + + for (size_t i = 0; i < evalColls.size(); i++) { + if (summarize) { + std::cout << evlFeedPaths[i] << ": "; + evalColls[i].printShortStats(&std::cout); + std::cout << std::endl; + } else { + std::cout << " == Evaluation results for " << evlFeedPaths[i] + << " ===" << std::endl; + evalColls[i].printStats(&std::cout); + } + } +} diff --git a/src/util/3rdparty/MurmurHash3.cpp b/src/util/3rdparty/MurmurHash3.cpp new file mode 100644 index 0000000..aa7982d --- /dev/null +++ b/src/util/3rdparty/MurmurHash3.cpp @@ -0,0 +1,335 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "MurmurHash3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +#define FORCE_INLINE __forceinline + +#include + +#define ROTL32(x,y) _rotl(x,y) +#define ROTL64(x,y) _rotl64(x,y) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define FORCE_INLINE inline __attribute__((always_inline)) + +inline uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +inline uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) +{ + return p[i]; +} + +FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i ) +{ + return p[i]; +} + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + + uint32_t h1 = seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock32(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix32(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + const uint32_t c1 = 0x239b961b; + const uint32_t c2 = 0xab0e9789; + const uint32_t c3 = 0x38b34ae5; + const uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock32(blocks,i*4+0); + uint32_t k2 = getblock32(blocks,i*4+1); + uint32_t k3 = getblock32(blocks,i*4+2); + uint32_t k4 = getblock32(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(int i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock64(blocks,i*2+0); + uint64_t k2 = getblock64(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= ((uint64_t)tail[14]) << 48; + case 14: k2 ^= ((uint64_t)tail[13]) << 40; + case 13: k2 ^= ((uint64_t)tail[12]) << 32; + case 12: k2 ^= ((uint64_t)tail[11]) << 24; + case 11: k2 ^= ((uint64_t)tail[10]) << 16; + case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; + case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; + case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; + case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; + case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; + case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; + case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; + case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; + case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- + diff --git a/src/util/3rdparty/MurmurHash3.h b/src/util/3rdparty/MurmurHash3.h new file mode 100644 index 0000000..e1c6d34 --- /dev/null +++ b/src/util/3rdparty/MurmurHash3.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) && (_MSC_VER < 1600) + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; + +// Other compilers + +#else // defined(_MSC_VER) + +#include + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); + +//----------------------------------------------------------------------------- + +#endif // _MURMURHASH3_H_ diff --git a/src/util/3rdparty/dtoa_milo.h b/src/util/3rdparty/dtoa_milo.h new file mode 100644 index 0000000..1ed2670 --- /dev/null +++ b/src/util/3rdparty/dtoa_milo.h @@ -0,0 +1,454 @@ +// based on +// https://github.com/miloyip/dtoa-benchmark/blob/master/src/milo/dtoa_milo.h + +#pragma once +#include +#include +#include + +#if defined(_MSC_VER) +#include +#include "msinttypes/stdint.h" +#else +#include +#endif + +namespace gcc_ints { +__extension__ typedef __int128 int128; +__extension__ typedef unsigned __int128 uint128; +} // namespace gcc_ints + +#define UINT64_C2(h, l) \ + ((static_cast(h) << 32) | static_cast(l)) + +namespace util { + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t f, int e) : f(f), e(e) {} + + DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = {d}; + + int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + assert(e == rhs.e); + assert(f >= rhs.f); + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \ + defined(__x86_64__) + gcc_ints::uint128 p = static_cast(f) * + static_cast(rhs.f); + uint64_t h = p >> 64; + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & kDpHiddenBit)) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 1); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1); + return res; +#endif + } + + DiyFp NormalizeBoundary() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#else + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; +#endif + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) + : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMinExponent = -kDpExponentBias; + static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPower(int e, int* K) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76), + UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea), + UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df), + UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f), + UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c), + UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5), + UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d), + UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637), + UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7), + UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5), + UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b), + UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996), + UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8), + UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053), + UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd), + UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94), + UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b), + UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac), + UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3), + UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb), + UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c), + UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000), + UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984), + UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70), + UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245), + UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8), + UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a), + UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea), + UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85), + UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2), + UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3), + UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25), + UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece), + UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5), + UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a), + UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a), + UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129), + UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429), + UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d), + UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841), + UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9), + UINT64_C2(0xaf87023b, 0x9bf0ee6b)}; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + + // int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast( + index << 3)); // decimal exponent no need lookup table + + assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0])); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, + uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in + // this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, + char* buffer, int* len, int* K) { + static const uint64_t kPow10[] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000}; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = static_cast(CountDecimalDigit32(p1)); + *len = 0; + + while (kappa > 0) { + uint32_t d; + switch (kappa) { + case 10: + d = p1 / 1000000000; + p1 %= 1000000000; + break; + case 9: + d = p1 / 100000000; + p1 %= 100000000; + break; + case 8: + d = p1 / 10000000; + p1 %= 10000000; + break; + case 7: + d = p1 / 1000000; + p1 %= 1000000; + break; + case 6: + d = p1 / 100000; + p1 %= 100000; + break; + case 5: + d = p1 / 10000; + p1 %= 10000; + break; + case 4: + d = p1 / 1000; + p1 %= 1000; + break; + case 3: + d = p1 / 100; + p1 %= 100; + break; + case 2: + d = p1 / 10; + p1 %= 10; + break; + case 1: + d = p1; + p1 = 0; + break; + default: +#if defined(_MSC_VER) + __assume(0); +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + __builtin_unreachable(); +#else + d = 0; +#endif + } + if (d || *len) buffer[(*len)++] = '0' + static_cast(d); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, + static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) buffer[(*len)++] = '0' + d; + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', + '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', + '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', + '7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', + '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5', + '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', + '7', '6', '8', '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', + '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', + '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', + '7', '9', '8', '9', '9'}; + return cDigitsLut; +} + +inline void WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = '0' + static_cast(K / 100); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } else + *buffer++ = '0' + static_cast(K); + + *buffer = '\0'; +} + +inline void Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + buffer[kk + 2] = '\0'; + } else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + memmove(&buffer[kk + 1], &buffer[kk], length - kk); + buffer[kk] = '.'; + buffer[length + 1] = '\0'; + } else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + memmove(&buffer[offset], &buffer[0], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) buffer[i] = '0'; + buffer[length + offset] = '\0'; + } else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + WriteExponent(kk - 1, &buffer[2]); + } else { + // 1234e30 -> 1.234e33 + memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline void dtoa_milo(double value, char* buffer) { + // Not handling NaN and inf + assert(!std::isnan(value)); + assert(!std::isinf(value)); + + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + buffer[3] = '\0'; + } else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + Prettify(buffer, length, K); + } +} +} // namespace util diff --git a/src/util/Misc.h b/src/util/Misc.h index 6bac94b..feb3207 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -9,18 +9,81 @@ #include #include #include +#include +#include +#include #include #include #include +#include +#include +#include "3rdparty/dtoa_milo.h" #define UNUSED(expr) do { (void)(expr); } while (0) #define TIME() std::chrono::high_resolution_clock::now() -#define TOOK(t1, t2) (std::chrono::duration_cast(t2 - t1).count() / 1000.0) +#define TOOK(t1, t2) (std::chrono::duration_cast(t2 - t1).count() / 1000.0) #define T_START(n) auto _tstart_##n = std::chrono::high_resolution_clock::now() -#define T_STOP(n) (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0) +#define T_STOP(n) (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0) + +#define _TEST3(s, o, e) if (!(s o e)) { std::cerr << "\n" << __FILE__ << ":" << __LINE__ << ": Test failed!\n Expected " << #s << " " << #o " " << (e) << ", got " << (s) << std::endl; exit(1);} +#define _TEST2(s, e) _TEST3(s, ==, o) +#define _TEST1(s) _TEST3(static_cast(s), ==, true) + +#define _GET_TEST_MACRO(_1,_2,_3,NAME,...) NAME +#define TEST(...) _GET_TEST_MACRO(__VA_ARGS__, _TEST3, _TEST2, _TEST1, UNUSED)(__VA_ARGS__) + +#define TODO(msg) std::cerr << "\n" __FILE__ << ":" << __LINE__ << ": TODO: " << #msg << std::endl; + +#if defined(_WIN32) +#include +#include +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#if defined(__APPLE__) && defined(__MACH__) +#include +#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) +#include +#include +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +#include +#endif +#else +#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." +#endif namespace util { +struct hashPair { + template + size_t operator()(const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + return h1 ^ h2; + } +}; + +template +class SparseMatrix { + public: + Val get(const Key& x, const Key& y) const { + auto a = _m.find(std::pair(x, y)); + if (a == _m.end()) return Def; + return a->second; + } + + void set(Key x, Key y, Val v) { + _m[std::pair(x, y)] = v; + } + + const std::map, Val>& vals() const { + return _m; + } + + private: + std::map, Val> _m; +}; + // cached first 10 powers of 10 static int pow10[10] = { 1, 10, 100, 1000, 10000, @@ -28,7 +91,7 @@ static int pow10[10] = { // _____________________________________________________________________________ inline uint64_t factorial(uint64_t n) { - if (n == 1) return n; + if (n < 2) return 1; return n * factorial(n - 1); } @@ -89,6 +152,82 @@ inline double atof(const char* p, uint8_t mn) { // _____________________________________________________________________________ inline double atof(const char* p) { return atof(p, 38); } +// _____________________________________________________________________________ +template +int merge(V* lst, V* tmpLst, size_t l, size_t m, size_t r) { + size_t ret = 0; + + size_t lp = l; + size_t rp = m; + size_t outp = l; + + while (lp < m && rp < r + 1) { + if (lst[lp] <= lst[rp]) { + // if left element is smaller or equal, add it to return list, + // increase left pointer + tmpLst[outp] = lst[lp]; + lp++; + } else { + // if left element is bigger, add the right element, add it to ret, + // increase right pointer + tmpLst[outp] = lst[rp]; + rp++; + + // if the left element was bigger, everything to the right in the + // left list is also bigger, and all these m - i elements were + // initially in the wrong order! Count these inversions. + ret += m - lp; + } + + outp++; + } + + // fill in remaining values + if (lp < m) std::memcpy(tmpLst + outp, lst + lp, (m - lp) * sizeof(V)); + if (rp <= r) std::memcpy(tmpLst + outp, lst + rp, ((r + 1) - rp) * sizeof(V)); + + // copy to output + std::memcpy(lst + l, tmpLst + l, ((r + 1) - l) * sizeof(V)); + + return ret; +} + +// _____________________________________________________________________________ +template +size_t mergeInvCount(V* lst, V* tmpLst, size_t l, size_t r) { + size_t ret = 0; + if (l < r) { + size_t m = (r + l) / 2; + + ret += mergeInvCount(lst, tmpLst, l, m); + ret += mergeInvCount(lst, tmpLst, m + 1, r); + + ret += merge(lst, tmpLst, l, m + 1, r); + } + return ret; +} + +// _____________________________________________________________________________ +template +size_t inversions(const std::vector& v) { + if (v.size() < 2) return 0; // no inversions possible + + // unroll some simple cases + if (v.size() == 2) return v[1] < v[0]; + if (v.size() == 3) return (v[0] > v[1]) + (v[0] > v[2]) + (v[1] > v[2]); + + auto tmpLst = new V[v.size()]; + auto lst = new V[v.size()]; + + for (size_t i = 0; i < v.size(); i++) lst[i] = v[i]; + + size_t ret = mergeInvCount(lst, tmpLst, 0, v.size() - 1); + delete[] tmpLst; + delete[] lst; + return ret; +} + + // _____________________________________________________________________________ inline std::string getHomeDir() { // parse implicit paths @@ -115,6 +254,29 @@ inline std::string getHomeDir() { return ret; } +// _____________________________________________________________________________ +inline char* readableSize(double size, char* buf) { + int i = 0; + const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB"}; + while (size > 1024 && i < 5) { + size /= 1024; + i++; + } + sprintf(buf, "%.*f %s", i, size, units[i]); + return buf; +} + +// _____________________________________________________________________________ +inline std::string readableSize(double size) { + char buffer[30]; + return readableSize(size, buffer); +} + +// _____________________________________________________________________________ +inline float f_rsqrt(float x) { + return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); +} + // _____________________________________________________________________________ inline std::string getTmpDir() { // first, check if an env variable is set @@ -131,6 +293,150 @@ inline std::string getTmpDir() { return getHomeDir(); } +// _____________________________________________________________________________ +class approx { + public: + explicit approx(double magnitude) + : _epsilon{std::numeric_limits::epsilon() * 100}, + _magnitude{magnitude} {} + + friend bool operator==(double lhs, approx const& rhs) { + return std::abs(lhs - rhs._magnitude) < rhs._epsilon; + } + + friend bool operator==(approx const& lhs, double rhs) { + return operator==(rhs, lhs); + } + + friend bool operator!=(double lhs, approx const& rhs) { + return !operator==(lhs, rhs); + } + + friend bool operator!=(approx const& lhs, double rhs) { + return !operator==(rhs, lhs); + } + + friend bool operator<=(double lhs, approx const& rhs) { + return lhs < rhs._magnitude || lhs == rhs; + } + + friend bool operator<=(approx const& lhs, double rhs) { + return lhs._magnitude < rhs || lhs == rhs; + } + + friend bool operator>=(double lhs, approx const& rhs) { + return lhs > rhs._magnitude || lhs == rhs; + } + + friend bool operator>=(approx const& lhs, double rhs) { + return lhs._magnitude > rhs || lhs == rhs; + } + + friend std::ostream& operator<< (std::ostream &out, const approx &a) { + out << "~" << a._magnitude; + return out; + } + + private: + double _epsilon; + double _magnitude; +}; + +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + */ + +/** + * Returns the peak (maximum so far) resident set size (physical + * memory use) measured in bytes, or zero if the value cannot be + * determined on this OS. + */ + +// _____________________________________________________________________________ +inline size_t getPeakRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.PeakWorkingSetSize; + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || \ + (defined(__sun__) || defined(__sun) || \ + defined(sun) && (defined(__SVR4) || defined(__svr4__))) + /* AIX and Solaris ------------------------------------------ */ + struct psinfo psinfo; + int fd = -1; + if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) + return (size_t)0L; /* Can't open? */ + if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { + close(fd); + return (size_t)0L; /* Can't read? */ + } + close(fd); + return (size_t)(psinfo.pr_rssize * 1024L); + +#elif defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + /* BSD, Linux, and OSX -------------------------------------- */ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); +#if defined(__APPLE__) && defined(__MACH__) + return (size_t)rusage.ru_maxrss; +#else + return (size_t)(rusage.ru_maxrss * 1024L); +#endif + +#else + /* Unknown OS ----------------------------------------------- */ + return (size_t)0L; /* Unsupported. */ +#endif +} + +/* + * Returns the current resident set size (physical memory use) measured + * in bytes, or zero if the value cannot be determined on this OS. + */ + +// _____________________________________________________________________________ +inline size_t getCurrentRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.WorkingSetSize; + +#elif defined(__APPLE__) && defined(__MACH__) + /* OSX ------------------------------------------------------ */ + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, + &infoCount) != KERN_SUCCESS) + return (size_t)0L; /* Can't access? */ + return (size_t)info.resident_size; + +#elif defined(__linux__) || defined(__linux) || defined(linux) || \ + defined(__gnu_linux__) + /* Linux ---------------------------------------------------- */ + long rss = 0L; + FILE* fp = NULL; + if ((fp = fopen("/proc/self/statm", "r")) == NULL) + return (size_t)0L; /* Can't open? */ + if (fscanf(fp, "%*s%ld", &rss) != 1) { + fclose(fp); + return (size_t)0L; /* Can't read? */ + } + fclose(fp); + return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); + +#else + /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ + return (size_t)0L; /* Unsupported. */ +#endif +} + } // namespace util #endif // UTIL_MISC_H_ diff --git a/src/util/PriorityQueue.h b/src/util/PriorityQueue.h new file mode 100644 index 0000000..13bd973 --- /dev/null +++ b/src/util/PriorityQueue.h @@ -0,0 +1,39 @@ +// Copyright 2019, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef UTIL_PRIORITYQUEUE_H_ +#define UTIL_PRIORITYQUEUE_H_ + +#include +#include +#include + +namespace util { +template +class PriorityQueue { + struct _ByFirst { + bool operator()(const std::pair& a, const std::pair& b) { + return a.first > b.first; + } + }; + + + public: + PriorityQueue() : _last(std::numeric_limits::lowest()) {} + void push(K k, const V& v); + const K topKey() ; + const V& topVal() ; + void pop(); + + bool empty() const; + + private: + K _last; + std::priority_queue, std::vector>, _ByFirst> + _pq; +}; +#include "util/PriorityQueue.tpp" +} // namespace util + +#endif diff --git a/src/util/PriorityQueue.tpp b/src/util/PriorityQueue.tpp new file mode 100644 index 0000000..a46adbb --- /dev/null +++ b/src/util/PriorityQueue.tpp @@ -0,0 +1,41 @@ +// Copyright 2019, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +// _____________________________________________________________________________ +template +void PriorityQueue::push(K key, const V& val) { + // if (_last - key > 0) key = _last; + if (key < _last) { + std::cout << std::setprecision(10) << _last << " vs " << key << std::endl; + key = _last; + } + // assert(key >= _last); + _pq.emplace(std::pair(key, val)); +} + +// _____________________________________________________________________________ +template +const K PriorityQueue::topKey() { + _last = _pq.top().first; + return _pq.top().first; +} + +// _____________________________________________________________________________ +template +const V& PriorityQueue::topVal() { + _last = _pq.top().first; + return _pq.top().second; +} + +// _____________________________________________________________________________ +template +bool PriorityQueue::empty() const { + return _pq.empty(); +} + +// _____________________________________________________________________________ +template +void PriorityQueue::pop() { + _pq.pop(); +} diff --git a/src/util/String.h b/src/util/String.h index 536b713..379785f 100644 --- a/src/util/String.h +++ b/src/util/String.h @@ -6,11 +6,17 @@ #define UTIL_STRING_H_ #include +#include +#include #include +#include #include +#include +#include #include #include #include +#include namespace util { @@ -169,7 +175,8 @@ inline size_t editDist(const std::string& s1, const std::string& s2) { } // _____________________________________________________________________________ -inline size_t prefixEditDist(const std::string& prefix, const std::string& s, +template +inline size_t prefixEditDist(const String& prefix, const String& s, size_t deltaMax) { // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++ size_t len1 = prefix.size(); @@ -200,10 +207,45 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s, } // _____________________________________________________________________________ -inline size_t prefixEditDist(const std::string& prefix, const std::string& s) { +template +inline size_t prefixEditDist(const String& prefix, const String& s) { return prefixEditDist(prefix, s, s.size()); } +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const char* s) { + return prefixEditDist(std::string(prefix), std::string(s)); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const char* s, + size_t deltaMax) { + return prefixEditDist(std::string(prefix), std::string(s), + deltaMax); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const std::string& s) { + return prefixEditDist(std::string(prefix), s); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const std::string& s, + size_t deltaMax) { + return prefixEditDist(std::string(prefix), s, deltaMax); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const std::string& prefix, const char* s) { + return prefixEditDist(prefix, std::string(s)); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const std::string& prefix, const char* s, + size_t deltaMax) { + return prefixEditDist(prefix, std::string(s), deltaMax); +} + // _____________________________________________________________________________ inline std::string toUpper(std::string str) { std::transform(str.begin(), str.end(), str.begin(), toupper); @@ -251,10 +293,66 @@ inline std::string normalizeWhiteSpace(const std::string& input) { } // _____________________________________________________________________________ -template +inline std::wstring toWStr(const std::string& str) { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +// _____________________________________________________________________________ +inline std::string toNStr(const std::wstring& wstr) { + std::wstring_convert> converter; + return converter.to_bytes(wstr); +} + +// _____________________________________________________________________________ +inline std::vector tokenize(const std::string& str) { + std::vector ret; + std::wstring wStr = toWStr(str); + + std::wstring cur; + for (size_t i = 0; i < wStr.size(); ++i) { + if (!std::iswalnum(wStr[i])) { + if (cur.size()) ret.push_back(toNStr(cur)); + cur = L""; + continue; + } + cur += wStr[i]; + } + if (cur.size()) ret.push_back(toNStr(cur)); + + return ret; +} + +// _____________________________________________________________________________ +inline double jaccardSimi(const std::string& a, + const std::string& b) { + std::set sa, sb; + + auto toksA = tokenize(a); + auto toksB = tokenize(b); + + // 0 if both are empty + if (toksA.size() == 0 && toksB.size() == 0) return 0; + + sa.insert(toksA.begin(), toksA.end()); + sb.insert(toksB.begin(), toksB.end()); + + std::set isect; + + std::set_intersection(sa.begin(), sa.end(), sb.begin(), sb.end(), + std::inserter(isect, isect.begin())); + + double sInter = isect.size(); + double s1 = sa.size(); + double s2 = sb.size(); + return sInter / (s1 + s2 - sInter); +} + +// _____________________________________________________________________________ +template inline std::string implode(const std::vector& vec, const char* del) { return implode(vec.begin(), vec.end(), del); } -} +} // namespace util #endif // UTIL_STRING_H_ diff --git a/src/util/geo/BezierCurve.tpp b/src/util/geo/BezierCurve.tpp index fb7e6ca..4f08a31 100644 --- a/src/util/geo/BezierCurve.tpp +++ b/src/util/geo/BezierCurve.tpp @@ -64,7 +64,7 @@ const PolyLine& BezierCurve::render(double d) { } // _____________________________________________________________________________ -double CubicPolynom::valueAt(double atx) const { +inline double CubicPolynom::valueAt(double atx) const { double dx = atx - x; return a + b * dx + c * dx * dx + d * dx * dx * dx; } diff --git a/src/util/geo/CircularSegment.h b/src/util/geo/CircularSegment.h new file mode 100644 index 0000000..b80e60d --- /dev/null +++ b/src/util/geo/CircularSegment.h @@ -0,0 +1,41 @@ +// Copyright 2016, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef UTIL_GEO_CIRCULARSEGMENT_H_ +#define UTIL_GEO_CIRCULARSEGMENT_H_ + +#include +#include "util/geo/Geo.h" +#include "util/geo/PolyLine.h" + +namespace util { +namespace geo { + +/** + * Circular segment + */ +template +class CircularSegment { + public: + CircularSegment(const Point& a, double ang, const Point& c); + + const PolyLine& render(double d); + + private: + // store the rendered polyline for quicker access + PolyLine _rendered; + + const Point& _a, _c; + double _renderD; + + double _ang, _rad, _s, _initAng; + + Point valueAt(double t) const; +}; + +#include "util/geo/CircularSegment.tpp" +} +} + +#endif // UTIL_GEO_BEZIERCURVE_H_ diff --git a/src/util/geo/CircularSegment.tpp b/src/util/geo/CircularSegment.tpp new file mode 100644 index 0000000..6058c68 --- /dev/null +++ b/src/util/geo/CircularSegment.tpp @@ -0,0 +1,51 @@ +// Copyright 2016, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +// _____________________________________________________________________________ +template +CircularSegment::CircularSegment(const Point& a, double ang, + const Point& c) : _a(a), _c(c), _renderD(0), _ang(ang) + { + + _rad = dist(a, c); + _s = fabs(_ang * _rad); + _initAng = angBetween(c, a); +} + +// _____________________________________________________________________________ +template +Point CircularSegment::valueAt(double ang) const { + double xPos = _c.getX() + _rad * cos(ang); + double yPos = _c.getY() + _rad * sin(ang); + return Point(xPos, yPos); +} + +// _____________________________________________________________________________ +template +const PolyLine& CircularSegment::render(double d) { + assert(d > 0); + if (fabs(d - _renderD) < 0.001) return _rendered; + _renderD = d; + + if (_s == 0) { + _rendered << _a << _a; + return _rendered; + } + + _rendered.empty(); + double n = _s / d, dt = 1 / n, t = 0; + + bool cancel = false; + while (true) { + _rendered << valueAt(_initAng + t * _ang); + t += dt; + if (cancel) break; + if (t > 1) { + t = 1; + cancel = true; + } + } + + return _rendered; +} diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index ffbfada..c11557c 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -51,6 +51,11 @@ typedef Polygon IPolygon; const static double EPSILON = 0.00001; const static double RAD = 0.017453292519943295; // PI/180 +const static double IRAD = 180.0 / M_PI; // 180 / PI +const static double AVERAGING_STEP = 20; + +const static double M_PER_DEG = 111319.4; + // _____________________________________________________________________________ template @@ -203,6 +208,14 @@ inline Polygon move(Polygon geo, double x, double y) { return geo; } +// _____________________________________________________________________________ +template +inline Box move(Box geo, double x, double y) { + geo.setLowerLeft(move(geo.getLowerLeft(), x, y)); + geo.setUpperRight(move(geo.getUpperRight(), x, y)); + return geo; +} + // _____________________________________________________________________________ template