From 4733b0c676fb247b6c7973acaac96276b2fd2769 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Sun, 3 Feb 2019 12:48:48 +0100 Subject: [PATCH] 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);