diff --git a/.clang-format b/.clang-format index bd274289..66b6161d 100644 --- a/.clang-format +++ b/.clang-format @@ -41,7 +41,7 @@ BraceWrapping: SplitEmptyNamespace: true AfterCaseLabel: true BreakBeforeBinaryOperators: All -BreakBeforeBraces: Custom +BreakBeforeBraces: Stroustrup BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeComma BreakBeforeTernaryOperators: true diff --git a/.github/workflows/build-matrix.yml b/.github/workflows/build-matrix.yml index 774a70d9..fa5087a0 100644 --- a/.github/workflows/build-matrix.yml +++ b/.github/workflows/build-matrix.yml @@ -10,14 +10,14 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, ubuntu-latest, macos-latest] # + os: [windows-latest, ubuntu-latest] # , macos-latest include: - os: windows-latest compiler: msvc - os: ubuntu-latest compiler: gcc - - os: macos-latest - compiler: clang +# - os: macos-latest +# compiler: clang steps: - name: Checkout code uses: actions/checkout@v4.1.4 @@ -42,3 +42,7 @@ jobs: - name: Build run: | cmake --build build --config Release + + - name: Test + run: | + cmake --build build --target test diff --git a/CMakeLists.txt b/CMakeLists.txt index 68a5ea9b..08469322 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,3 +54,4 @@ add_subdirectory(schedule/test) add_subdirectory(schedule/benchmarks) add_subdirectory(raptor) add_subdirectory(raptor/test) +add_subdirectory(gtfsRaptorConfig) diff --git a/geometry/include/utils/utils.h b/geometry/include/utils/utils.h index c6bc097c..b277e6f1 100644 --- a/geometry/include/utils/utils.h +++ b/geometry/include/utils/utils.h @@ -7,7 +7,7 @@ #include #include #include -#include <../../include/spatial/CoordinateComponent.h> +#include constexpr long double operator"" _km(const long double value) { return value; diff --git a/geometry/test/CMakeLists.txt b/geometry/test/CMakeLists.txt index 12780c8f..da1b3be8 100644 --- a/geometry/test/CMakeLists.txt +++ b/geometry/test/CMakeLists.txt @@ -3,6 +3,7 @@ project("geometry_test" ) +include(CTest) enable_testing() add_executable(${PROJECT_NAME}) @@ -24,7 +25,9 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ) set(BINARY ${PROJECT_NAME}) -add_test(NAME ${BINARY} COMMAND ${BINARY}) + +add_test(NAME geomety COMMAND ${BINARY}_test) +add_test(NAME TestK2dTree COMMAND ${BINARY}_test) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/logging/src) diff --git a/geometry/test/test_kdTree.cpp b/geometry/test/test_kdTree.cpp index 7148f789..3199b8fc 100644 --- a/geometry/test/test_kdTree.cpp +++ b/geometry/test/test_kdTree.cpp @@ -3,7 +3,7 @@ // #include -#include "../../logging/src/LoggingPool.h" +#include "LoggerFactory.h" #include "utils.h" #include "usings.h" @@ -11,7 +11,8 @@ #include -TEST(geomety, TestK2dTree) { +TEST(geomety, TestK2dTree) +{ auto kdTree = geometry::kd_tree::spatialK2dTree({}); const auto firstCoordinate = geometry::kd_tree::spatialCoordinate(1.0); const auto secondCoordinate = geometry::kd_tree::spatialCoordinate(2.0); @@ -21,8 +22,7 @@ TEST(geomety, TestK2dTree) { } -class TestK2dTree : public testing::Test -{ +class TestK2dTree : public testing::Test { using hasher = geometry::utils::CoordinateComponentHash; using equalizer = geometry::utils::CoordinateComponentEqual; @@ -30,9 +30,10 @@ class TestK2dTree : public testing::Test std::unordered_map stations; std::unique_ptr kdTree; - void SetUp() override { + void SetUp() override + { // arrange - LoggingPool::getInstance().getLogger(Target::CONSOLE)->setLevel(LoggerBridge::ERROR); + getConsoleLogger(LoggerName::GEOMETRY)->info("TestK2dTree::SetUp"); stations[geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.4230127859742), geometry::kd_tree::spatialCoordinate(9.369479194531577))] = "St. Gallen Train Station"; stations[geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.41190313108875), geometry::kd_tree::spatialCoordinate(9.25303543394843))] = "Gossau Train Station"; stations[geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.41549705750434), geometry::kd_tree::spatialCoordinate(9.18936624689178))] = "Flawil Train Station"; @@ -40,8 +41,7 @@ class TestK2dTree : public testing::Test stations[geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.5002860698031), geometry::kd_tree::spatialCoordinate(8.723829255246654))] = "Wintherthur Train Station"; std::vector coordinates; - for (const auto& coordinate : stations | std::views::keys) - { + for (const auto& coordinate : stations | std::views::keys) { coordinates.push_back(coordinate); } @@ -49,23 +49,25 @@ class TestK2dTree : public testing::Test } }; -TEST_F(TestK2dTree, TestK2dTreeStGallenToWinterthur) { +TEST_F(TestK2dTree, TestK2dTreeStGallenToWinterthur) +{ // act const auto findNearestFromRickenbach = geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.53556052653995), geometry::kd_tree::spatialCoordinate(8.789113533722448)); const auto findNeaerestFromKreuzbleiche = geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.419620275547636), geometry::kd_tree::spatialCoordinate(9.359281921519944)); const auto nearestRickenbach = kdTree->nearest(findNearestFromRickenbach); - LoggingPool::getInstance().getLogger(Target::CONSOLE)->info(std::format("Nearest to Rickenbach is: {}", stations[nearestRickenbach])); + getConsoleLogger(LoggerName::GEOMETRY)->info(std::format("Nearest to Rickenbach is: {}", stations[nearestRickenbach])); const auto neaerestKreuzbleiche = kdTree->nearest(findNeaerestFromKreuzbleiche); - LoggingPool::getInstance().getLogger(Target::CONSOLE)->info(std::format("Nearest to Kreuzbleiche is: {}", stations[neaerestKreuzbleiche])); + getConsoleLogger(LoggerName::GEOMETRY)->info(std::format("Nearest to Kreuzbleiche is: {}", stations[neaerestKreuzbleiche])); // assert ASSERT_TRUE(stations[nearestRickenbach] == "Wintherthur Train Station"); ASSERT_TRUE(stations[neaerestKreuzbleiche] == "St. Gallen Train Station"); } -TEST_F(TestK2dTree, TestK2dTreeRangeSearch) { +TEST_F(TestK2dTree, TestK2dTreeRangeSearch) +{ // act const auto findNeaerestFromKreuzbleiche = geometry::kd_tree::coordinateComponent(geometry::kd_tree::spatialCoordinate(47.419620275547636), geometry::kd_tree::spatialCoordinate(9.359281921519944)); @@ -73,11 +75,9 @@ TEST_F(TestK2dTree, TestK2dTreeRangeSearch) { const auto nearestRickenbach = kdTree->rangeSearch(findNeaerestFromKreuzbleiche, rangeInKiloMeters); // assert - for (auto it = nearestRickenbach.begin(); it != nearestRickenbach.end(); ++it) - { - LoggingPool::getInstance().getLogger(Target::CONSOLE)->info(std::format("Nearest to Kreuzbleiche is: {}", stations[*it])); - switch (std::distance(nearestRickenbach.begin(), it)) - { + for (auto it = nearestRickenbach.begin(); it != nearestRickenbach.end(); ++it) { + getConsoleLogger(LoggerName::GEOMETRY)->info(std::format("Nearest to Kreuzbleiche is: {}", stations[*it])); + switch (std::distance(nearestRickenbach.begin(), it)) { case 0: ASSERT_TRUE(stations[*it] == "St. Gallen Train Station"); break; diff --git a/gtfsRaptorConfig/CMakeLists.txt b/gtfsRaptorConfig/CMakeLists.txt new file mode 100644 index 00000000..56c5da84 --- /dev/null +++ b/gtfsRaptorConfig/CMakeLists.txt @@ -0,0 +1,70 @@ +project(gtfsRaptorConfig) + +add_executable(gtfsSubsetWriter + src/agencySubsetWriter/gtfsSubsetWriter.cpp +) + +find_path(P_RANAV_CSV2_INCLUDE_DIRS "csv2/mio.hpp") + +add_library(${PROJECT_NAME} SHARED ) + +target_sources(${PROJECT_NAME} PRIVATE + src/GtfsToRaptorConverter.cpp + src/RoutePartitioner.cpp + src/TimetableManager.cpp + src/SubRoute.cpp + PUBLIC + include/GtfsToRaptorConverter.h + include/RoutePartitioner.h + include/TimetableManager.h + include/SubRoute.h +) + +target_include_directories(gtfsSubsetWriter PRIVATE ${CMAKE_SOURCE_DIR}/logging/include) +target_include_directories(gtfsSubsetWriter PRIVATE ${CMAKE_SOURCE_DIR}/gtfsRaptorConfig/src) +target_include_directories(gtfsSubsetWriter PRIVATE ${CMAKE_SOURCE_DIR}/gtfsRaptorConfig/include) +target_include_directories(gtfsSubsetWriter PUBLIC + $ + $ +) +target_include_directories(gtfsSubsetWriter PRIVATE ${P_RANAV_CSV2_INCLUDE_DIRS}) + +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/gtfsRaptorConfig/src) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/gtfsRaptorConfig/include) # check if public is needed +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/schedule/include) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/logging/include) +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + logging + geometry + schedule +) + +target_link_libraries(gtfsSubsetWriter PRIVATE + logging + geometry + schedule +) + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + +include(GenerateExportHeader) +generate_export_header(${PROJECT_NAME} + BASE_NAME ${PROJECT_NAME} + EXPORT_MACRO_NAME GTFS_RAPTOR_API + EXPORT_FILE_NAME ${PROJECT_NAME}_export.h + STATIC_DEFINE ${PROJECT_NAME}_BUILT_AS_STATIC +) + +set(INCLUDE_INSTALL_DIR include) + +install(FILES + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_export.h + DESTINATION + ${INCLUDE_INSTALL_DIR} +) \ No newline at end of file diff --git a/gtfsRaptorConfig/include/GtfsToRaptorConverter.h b/gtfsRaptorConfig/include/GtfsToRaptorConverter.h new file mode 100644 index 00000000..91e3b25a --- /dev/null +++ b/gtfsRaptorConfig/include/GtfsToRaptorConverter.h @@ -0,0 +1,15 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +#include + +namespace converter { + + class GTFS_RAPTOR_API GtfsToRaptorConverter + { + }; + +} // converter diff --git a/gtfsRaptorConfig/include/RoutePartitioner.h b/gtfsRaptorConfig/include/RoutePartitioner.h new file mode 100644 index 00000000..daae00f5 --- /dev/null +++ b/gtfsRaptorConfig/include/RoutePartitioner.h @@ -0,0 +1,44 @@ +// +// Created by MichaelBrunner on 19/07/2024. +// + +#ifndef ROUTEPARTITIONER_H +#define ROUTEPARTITIONER_H + +#include "GtfsData.h" +#include "model/Route.h" + +#include +#include +#include +#include + +namespace converter { + class SubRoute; + + class GTFS_RAPTOR_API RoutePartitioner + { + + public: + explicit RoutePartitioner(schedule::gtfs::GtfsData* data); + + [[nodiscard]] std::vector getSubRoutes(std::string const& routeId) const; + + [[nodiscard]] const SubRoute& getSubRoute(std::string const& tripId) const; + + + private: + std::unordered_map /*, decltype(routeHash), decltype(routeEqual)*/> subRoutes{}; + schedule::gtfs::GtfsData* data; + + void processRoute(schedule::gtfs::Route const& route); + + [[nodiscard]] std::string generateStopSequenceKey(const std::string& tripId) const; + + [[nodiscard]] std::vector extractStopSequence(schedule::gtfs::Trip const& aTrip) const; + }; + +} // gtfs +// schedule + +#endif //ROUTEPARTITIONER_H diff --git a/gtfsRaptorConfig/include/SubRoute.h b/gtfsRaptorConfig/include/SubRoute.h new file mode 100644 index 00000000..ce416419 --- /dev/null +++ b/gtfsRaptorConfig/include/SubRoute.h @@ -0,0 +1,60 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#pragma once + +#include "GtfsData.h" + +#include +#include + +namespace converter { + +class GTFS_RAPTOR_API SubRoute + { + std::string SubRouteId{}; + std::string routeId{}; + std::string stopSequenceKey{}; + std::vector stopsSequence{}; + std::vector trips{}; + + public: + explicit SubRoute(std::string&& subRouteId, std::string routeId, std::string stopSequenceKey, std::vector&& stopsSequence); + + SubRoute(const SubRoute& aSubRoute); + + SubRoute(SubRoute&& aSubRoute) noexcept = default; + SubRoute& operator=(const SubRoute& aSubRoute) = default; + SubRoute& operator=(SubRoute&& aSubRoute) noexcept = default; + + void addTrip(schedule::gtfs::Trip const& trip); + + [[nodiscard]] const std::string& getSubRouteId() const ; + + [[nodiscard]] const std::string& getRouteId() const; + + [[nodiscard]] const std::string& getStopSequenceKey() const { + return stopSequenceKey; + } + + [[nodiscard]] const std::vector& getStopsSequence() const; + + [[nodiscard]] size_t stopIndex(std::string_view stopId) const ; + + [[nodiscard]] const std::vector& getTrips() const; + + bool operator==(const SubRoute& aSubRoute) const; + + std::function SubRouteHash = [](const SubRoute& aSubRoute) { + return std::hash{}(aSubRoute.getSubRouteId()); + }; + + std::function SubRouteEqual = [](const SubRoute& aSubRoute, const SubRoute& anotherSubRoute) { + return aSubRoute == anotherSubRoute; + }; + }; + + + +} // converter diff --git a/gtfsRaptorConfig/include/TimetableManager.h b/gtfsRaptorConfig/include/TimetableManager.h new file mode 100644 index 00000000..ecf10924 --- /dev/null +++ b/gtfsRaptorConfig/include/TimetableManager.h @@ -0,0 +1,68 @@ +// +// Created by MichaelBrunner on 13/06/2024. +// + +#ifndef RELATIONMANAGER_H +#define RELATIONMANAGER_H + +#include "GtfsData.h" +#include "RoutePartitioner.h" + +#include +#include "model/StopTime.h" +#include "model/Trip.h" +#include "model/Stop.h" +#include "model/Route.h" + +#include +#include +#include + +namespace converter { + + class GTFS_RAPTOR_API TimetableManager + { + std::unique_ptr data; + std::unique_ptr routePartitioner; + + public: + explicit TimetableManager(schedule::gtfs::GtfsData&& data); + + [[nodiscard]] const schedule::gtfs::GtfsData& getData() const; + + [[nodiscard]] schedule::gtfs::GtfsData& getData(); + + [[nodiscard]] RoutePartitioner& getRoutePartitioner(); + + [[nodiscard]] const RoutePartitioner& getRoutePartitioner() const ; + + [[nodiscard]] const std::string& getStopNameFromStopId(std::string const& aStopId) const; + + [[nodiscard]] const std::string& getStopIdFromStopName(std::string const& aStopName) const; + + [[nodiscard]] std::vector getStopIdsFromStopName(std::string const& aStopName) const; + + [[nodiscard]] const schedule::gtfs::StopTime& getStopTimeFromStopId(std::string const& aStopId) const; + + [[nodiscard]] std::vector getAllStopsOnTrip(std::string const& aTripId) const; + + [[nodiscard]] const std::vector& getStopTimesFromStopId(std::string const& aStopId) const; + + [[nodiscard]] schedule::gtfs::Route getRouteFromTripId(std::string const& aTripId) const; + + [[nodiscard]] std::vector getStopTimesFromStopIdStartingFromSpecificTime(std::string const& aStopId, unsigned int secondsGreaterThan) const; + + [[nodiscard]] bool isServiceActiveOnDay(std::string const& aServiceId, std::chrono::weekday aDay) const; + + [[nodiscard]] const schedule::gtfs::Trip& getTripsFromStopTimeTripId(std::string const& aTripId) const; + + [[nodiscard]] std::vector getVisitedStopIds() const; + + private: + void createRelations() const; + void buildTripsToRoutesRelations() const; + void buildStopTimesToTripsAndRoutesRelations() const; + }; +} // gtfs + +#endif //RELATIONMANAGER_H diff --git a/gtfsRaptorConfig/src/GtfsToRaptorConverter.cpp b/gtfsRaptorConfig/src/GtfsToRaptorConverter.cpp new file mode 100644 index 00000000..4eca0cfe --- /dev/null +++ b/gtfsRaptorConfig/src/GtfsToRaptorConverter.cpp @@ -0,0 +1,8 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#include "GtfsToRaptorConverter.h" + +namespace converter { +} // converter \ No newline at end of file diff --git a/gtfsRaptorConfig/src/RoutePartitioner.cpp b/gtfsRaptorConfig/src/RoutePartitioner.cpp new file mode 100644 index 00000000..58eb452e --- /dev/null +++ b/gtfsRaptorConfig/src/RoutePartitioner.cpp @@ -0,0 +1,143 @@ +// +// Created by MichaelBrunner on 19/07/2024. +// + +#include "SubRoute.h" +#include "RoutePartitioner.h" + +#include +#include +#include + + +namespace converter { + + RoutePartitioner::RoutePartitioner(schedule::gtfs::GtfsData* data) + : data(data) { + + // auto routes = data->routes | std::views::values; + // std::vector routeList(routes.begin(), routes.end()); + // std::for_each(std::execution::par, routeList.begin(), routeList.end(), [this](Route const& route) { + // this->processRoute(route); + // }); + + std::ranges::for_each(data->routes | std::views::values, [this](schedule::gtfs::Route const& route) { + this->processRoute(route); + }); + } + + std::vector RoutePartitioner::getSubRoutes(std::string const& routeId) const { + const auto& subRoutesForRoute = subRoutes.at(routeId); + return std::ranges::views::values(subRoutesForRoute) | std::ranges::to>(); + } + + const SubRoute& RoutePartitioner::getSubRoute(std::string const& tripId) const { + const auto route = data->routes.at(tripId); + const auto& subRoutesForRoute = subRoutes.at(route.routeId); + const auto key = generateStopSequenceKey(tripId); + return subRoutesForRoute.at(key); + } + + void RoutePartitioner::processRoute(schedule::gtfs::Route const& route) { + std::unordered_map sequenceKeyToSubRoute; + sequenceKeyToSubRoute.reserve(data->routes.at(route.routeId).trips.size()); + + for (const auto& tripId : data->routes.at(route.routeId).trips) + { + const schedule::gtfs::Trip& trip = data->trips.at(tripId); + auto key = this->generateStopSequenceKey(tripId); + + auto [it, inserted] = sequenceKeyToSubRoute.try_emplace(key, + SubRoute(std::format("{}_sr{}", route.routeId, sequenceKeyToSubRoute.size() + 1), route.routeId, key, extractStopSequence(trip))); + if (!inserted) + { + it->second.addTrip(trip); + } + } + subRoutes.insert_or_assign(route.routeId, std::move(sequenceKeyToSubRoute)); + } + + + // void RoutePartitioner::processRoute(Route const& route) { + // std::unordered_map sequenceKeyToSubRoute{}; + // std::vector trips; + // std::ranges::transform(data->routes.at(route.routeId).trips, std::back_inserter(trips), [&](std::string const& tripId) { + // return data->trips.at(tripId); + // }); + + // std::ranges::for_each(trips, [&, this](Trip const& trip) { + // auto key = this->generateStopSequenceKey(trip); + // if (!sequenceKeyToSubRoute.contains(key)) + // { + // auto subRouteId = std::format("{}_sr{}", route.routeId, sequenceKeyToSubRoute.size() + 1); + // sequenceKeyToSubRoute.insert_or_assign(key, SubRoute(std::move(subRouteId), route.routeId, std::move(key), std::move(extractStopSequence(trip)))); + // } + // sequenceKeyToSubRoute.at(key).addTrip(trip); + // }); + // subRoutes.insert_or_assign(route.routeId, sequenceKeyToSubRoute); + // } + + std::string RoutePartitioner::generateStopSequenceKey(const std::string& tripId) const { + const auto range = data->trips.at(tripId).stopTimes; + std::string sequenceKey; + sequenceKey.reserve(std::distance(range.begin(), range.end()) * 401); // 400 + 1 for "_" + + bool first = true; + for (auto it = range.begin(); it != range.end(); ++it) + { + if (!first) + { + sequenceKey += "_"; + } + else + { + first = false; + } + sequenceKey += it->stopId; + } + return sequenceKey; + } + + + // std::string RoutePartitioner::generateStopSequenceKey(const Trip& trip) const { + // std::string sequenceKey; + // + // auto stopTimesForTripId = this->data->stopTimes + // | std::views::values + // | std::views::join + // | std::views::filter([&trip](const StopTime& stopTime) { + // return stopTime.tripId == trip.tripId; + // }); + // + // for (const auto& stopTime : stopTimesForTripId) + // { + // if (!sequenceKey.empty()) + // { + // sequenceKey += "_"; + // } + // sequenceKey += stopTime.stopId; + // } + // return sequenceKey; + // } + + std::vector RoutePartitioner::extractStopSequence(schedule::gtfs::Trip const& aTrip) const { + std::vector stops; + const auto range = data->trips.at(aTrip.tripId).stopTimes; + stops.reserve(std::distance(range.begin(), range.end())); + + for (const auto& it : range) + { + stops.push_back(data->stops.at(it.stopId)); + } + return stops; + } + + + /*std::vector RoutePartitioner::extractStopSequence(Trip const& aTrip) const { + return data->stopTimes | std::views::values | std::views::join + | std::views::filter([&](const StopTime& stopTime) { return stopTime.tripId == aTrip.tripId; }) + | std::views::transform([&](const StopTime& stopTime) { return data->stops.at(stopTime.stopId); }) + | std::ranges::to>(); + }*/ +} // gtfs + // schedule \ No newline at end of file diff --git a/gtfsRaptorConfig/src/SubRoute.cpp b/gtfsRaptorConfig/src/SubRoute.cpp new file mode 100644 index 00000000..325b00f2 --- /dev/null +++ b/gtfsRaptorConfig/src/SubRoute.cpp @@ -0,0 +1,57 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#include "SubRoute.h" + +namespace converter { + + SubRoute::SubRoute(std::string&& subRouteId, std::string routeId, std::string stopSequenceKey, std::vector&& stopsSequence) + : SubRouteId(std::move(subRouteId)) + , routeId(std::move(routeId)) + , stopSequenceKey(std::move(stopSequenceKey)) + , stopsSequence(std::move(stopsSequence)) { + } + + SubRoute::SubRoute(const SubRoute& aSubRoute) + : SubRouteId(aSubRoute.SubRouteId) + , stopSequenceKey(aSubRoute.stopSequenceKey) + , stopsSequence(aSubRoute.stopsSequence) + , trips(aSubRoute.trips) { + } + + void SubRoute::addTrip(schedule::gtfs::Trip const& trip) { + trips.push_back(trip); + } + + const std::string& SubRoute::getSubRouteId() const { + return SubRouteId; + } + + const std::string& SubRoute::getRouteId() const { + return routeId; + } + + const std::vector& SubRoute::getStopsSequence() const { + return stopsSequence; + } + + size_t SubRoute::stopIndex(const std::string_view stopId) const { + for (size_t i = 0; i < stopsSequence.size(); ++i) + { + if (stopsSequence[i].stopId == stopId) + { + return i; + } + } + return stopsSequence.size(); + } + + const std::vector& SubRoute::getTrips() const { + return trips; + } + + bool SubRoute::operator==(const SubRoute& aSubRoute) const { + return SubRouteId == aSubRoute.getSubRouteId(); // && stopSequenceKey == aSubRoute.stopSequenceKey + } +} // converter \ No newline at end of file diff --git a/gtfsRaptorConfig/src/TimetableManager.cpp b/gtfsRaptorConfig/src/TimetableManager.cpp new file mode 100644 index 00000000..8624d40f --- /dev/null +++ b/gtfsRaptorConfig/src/TimetableManager.cpp @@ -0,0 +1,140 @@ +// +// Created by MichaelBrunner on 13/06/2024. +// + +#include "TimetableManager.h" + +#include +#include +#include + + +namespace converter { + + TimetableManager::TimetableManager(schedule::gtfs::GtfsData&& data) + : data(std::make_unique(std::move(data))) { + createRelations(); + routePartitioner = std::make_unique(this->data.get()); + } + + void TimetableManager::createRelations() const { + buildTripsToRoutesRelations(); + buildStopTimesToTripsAndRoutesRelations(); + } + + void TimetableManager::buildTripsToRoutesRelations() const { + std::ranges::for_each(data->trips | std::views::values, [this](const schedule::gtfs::Trip& trip) { + data->routes.at(trip.routeId).trips.push_back(trip.tripId); + }); + } + + void TimetableManager::buildStopTimesToTripsAndRoutesRelations() const { + std::string scannedTripId{}; + bool addStops{false}; + + for (const auto& stopTime : data->stopTimes | std::views::values | std::views::join) + { + // Add stopTime to trip + schedule::gtfs::Trip& tripServingStopTime = data->trips.at(stopTime.tripId); + tripServingStopTime.stopTimes.push_back(stopTime); + // tripServingStopTime.stopTimes.push_back(&stopTime); + // TODO check if this is necessary + // tripServingStopTime.addTimesAtStop(stopTime.arrivalTime.toSeconds(), stopTime.departureTime.toSeconds()); + + // Stop that is served by the stopTime + // Add stopTime to route and stop if not already present + // if (Stop& stopServedByStopTime = data->stops.at(stopTime.stopId); + // std::ranges::find_if(stopServedByStopTime.routesServedByStop, [&tripServingStopTime](const std::string& routeId) { return routeId == tripServingStopTime.routeId; }) + // == stopServedByStopTime.routesServedByStop.end()) + // { + // stopServedByStopTime.routesServedByStop.push_back(tripServingStopTime.routeId); + // } + } + } + + const schedule::gtfs::GtfsData& TimetableManager::getData() const { + return *data; + } + + schedule::gtfs::GtfsData& TimetableManager::getData() { + return *data; + } + RoutePartitioner& TimetableManager::getRoutePartitioner() { + return *routePartitioner; + } + + const RoutePartitioner& TimetableManager::getRoutePartitioner() const { + return *routePartitioner; + } + + const std::string& TimetableManager::getStopNameFromStopId(std::string const& aStopId) const { + return data->stops.at(aStopId).stopName; + } + + const std::string& TimetableManager::getStopIdFromStopName(std::string const& aStopName) const { + return std::ranges::find_if(data->stops, + [&aStopName](const auto& stop) { return stop.second.stopName == aStopName; }) + ->first; + } + + std::vector TimetableManager::getStopIdsFromStopName(std::string const& aStopName) const { + return data->stops | std::views::filter([&aStopName](const auto& stop) { return stop.second.stopName == aStopName; }) + | std::views::keys | std::ranges::to>(); + } + const schedule::gtfs::StopTime& TimetableManager::getStopTimeFromStopId(std::string const& aStopId) const { + return data->stopTimes.at(aStopId).front(); + } + + std::vector TimetableManager::getAllStopsOnTrip(std::string const& aTripId) const { + // Retrieve all StopTimes for the given tripId, sorted by departure time + auto stopTimesMatchingTripId + = data->stopTimes | std::views::values | std::views::join + | std::views::filter([&](const schedule::gtfs::StopTime& stopTime) { return stopTime.tripId == aTripId; }) + | std::ranges::to>(); + + std::ranges::sort(stopTimesMatchingTripId, + [](const schedule::gtfs::StopTime& a, const schedule::gtfs::StopTime& b) { return a.departureTime < b.departureTime; }); + + std::vector stopsForTrip; + for (const auto& stopTime : stopTimesMatchingTripId) + { stopsForTrip.push_back(data->stops.at(stopTime.stopId)); } + + return stopsForTrip; + } + + const std::vector& TimetableManager::getStopTimesFromStopId(std::string const& aStopId) const { + return data->stopTimes.at(aStopId); + } + + schedule::gtfs::Route TimetableManager::getRouteFromTripId(std::string const& aTripId) const { + const auto& trip = data->trips.at(aTripId); + return data->routes.at(trip.routeId); + + // return data->trips.at(aTripId) + // | std::views::transform([this](const Trip& trip) { return data->routes.at(trip.routeId); }) + // | std::ranges::to>(); + } + std::vector TimetableManager::getStopTimesFromStopIdStartingFromSpecificTime(std::string const& aStopId, unsigned int secondsGreaterThan) const { + return data->stopTimes.at(aStopId) + | std::views::filter([secondsGreaterThan, this](const schedule::gtfs::StopTime& stopTime) { + return stopTime.departureTime.toSeconds() > secondsGreaterThan; + }) + | std::ranges::to>(); + } + + bool TimetableManager::isServiceActiveOnDay(std::string const& aServiceId, const std::chrono::weekday aDay) const { + return data->calendars.at(aServiceId).weekdayService.contains(aDay); + } + + const schedule::gtfs::Trip& TimetableManager::getTripsFromStopTimeTripId(std::string const& aTripId) const { + return data->trips.at(aTripId); + } + + std::vector TimetableManager::getVisitedStopIds() const { + // return std::ranges::views::keys(data->stops) + // | std::views::filter([&](const std::string& stopId) { return data->stops.at(stopId).visited; }) + // | std::ranges::to>(); + return {}; + } + +} // gtfs diff --git a/schedule/src/gtfs/agencySubsetWriter/gtfsSubsetWriter.cpp b/gtfsRaptorConfig/src/agencySubsetWriter/gtfsSubsetWriter.cpp similarity index 81% rename from schedule/src/gtfs/agencySubsetWriter/gtfsSubsetWriter.cpp rename to gtfsRaptorConfig/src/agencySubsetWriter/gtfsSubsetWriter.cpp index 6e9b1456..cca02556 100644 --- a/schedule/src/gtfs/agencySubsetWriter/gtfsSubsetWriter.cpp +++ b/gtfsRaptorConfig/src/agencySubsetWriter/gtfsSubsetWriter.cpp @@ -6,9 +6,9 @@ #include "DataReader.h" #include "GtfsReaderStrategyFactory.h" #include "LoggerFactory.h" -#include "gtfs/GtfsData.h" +#include "GtfsData.h" #include "gtfs/GtfsTxtReaderStrategyFactory.h" -#include "gtfs/RelationManager.h" +#include "TimetableManager.h" #include "utils/DataContainer.h" @@ -41,8 +41,9 @@ int main(int argc, char* argv[]) { for (int i = 0; i < argc; ++i) { std::cerr << "argv[" << i << "] = " << argv[i] << '\n'; + getConsoleLogger(LoggerName::GTFS)->error(std::format("argv[{}] = {}", i, argv[i])); } - std::cerr << "Usage: " << argv[0] << " " << '\n'; + getConsoleLogger(LoggerName::GTFS)->error("Usage: " + std::string(argv[0]) + " "); return EXIT_FAILURE; } @@ -54,9 +55,11 @@ int main(int argc, char* argv[]) { } std::string agencyName = argv[2]; + auto dataDirectoryToWriteFilesTo = dataDirectoryPath; + std::map lFileNameMap; - auto readerFactory = schedule::gtfs::createGtfsReaderStrategyFactory(schedule::gtfs::ReaderType::CSV, std::move(dataDirectoryPath)); + auto readerFactory = schedule::gtfs::createGtfsReaderStrategyFactory(schedule::gtfs::ReaderType::CSV_PARALLEL, std::move(dataDirectoryPath)); const auto agencyStrategy = readerFactory->getStrategy(GtfsStrategyType::AGENCY); const auto calendarStrategy = readerFactory->getStrategy(GtfsStrategyType::CALENDAR); @@ -71,7 +74,7 @@ int main(int argc, char* argv[]) { const std::unique_ptr>> reader = std::make_unique(std::move(strategies)); reader->readData(); - auto relationManager = schedule::gtfs::RelationManager(std::move(reader->getData().get())); + auto relationManager = schedule::gtfs::TimetableManager(std::move(reader->getData().get())); const auto& agency = relationManager.getData().agencies.at(agencyName); getLogger(Target::CONSOLE, LoggerName::GTFS)->info(agency.name); @@ -98,9 +101,10 @@ int main(int argc, char* argv[]) { for (const auto& trip : route.trips) { - trips.push_back(trip); + const auto& currentTrip = data.trips.at(trip); + trips.push_back(currentTrip); - auto calendarIterator = allCalendars.find(trip.serviceId); + auto calendarIterator = allCalendars.find(currentTrip.serviceId); if (calendarIterator != allCalendars.end()) { const auto& calendar = calendarIterator->second; @@ -121,10 +125,14 @@ int main(int argc, char* argv[]) { } else { - getLogger(Target::CONSOLE, LoggerName::GTFS)->warn("No calendar found for service id: " + trip.serviceId); + getLogger(Target::CONSOLE, LoggerName::GTFS)->warn("No calendar found for service id: " + currentTrip.serviceId); } - for (const auto& stopTime : trip.stopTimes) + auto stopTimesForTrip = data.stopTimes | std::views::values | std::views::join | std::views::filter([&trip](const auto& stopTime) { + return stopTime.tripId == trip; + }); + + for (const auto& stopTime : stopTimesForTrip) { stopTimes.push_back(stopTime); @@ -158,27 +166,27 @@ int main(int argc, char* argv[]) { } } - std::vector routesHeader = {"route_id", "agency_id", "route_short_name", "route_long_name", "route_type"}; + std::vector routesHeader = {"route_id", "agency_id", "route_short_name", "route_long_name", "route_desc", "route_type"}; std::vector calendarHeader = {"service_id", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "start_date", "end_date"}; std::vector calendarDatesHeader = {"service_id", "date", "exception_type"}; - std::vector stopsHeader = {"stop_id", "stop_name", "stop_lat", "stop_lon", "zone_id", "parent_station"}; - std::vector stopTimesHeader = {"trip_id", "arrival_time", "departure_time", "stop_id", "stop_sequence"}; - std::vector tripsHeader = {"route_id", "service_id", "trip_id", "trip_headsign", "trip_short_name", "direction_id"}; + std::vector stopsHeader = {"stop_id", "stop_name", "stop_lat", "stop_lon", "location_type", "parent_station"}; + std::vector stopTimesHeader = {"trip_id", "arrival_time", "departure_time", "stop_id", "stop_sequence", "pickup_type", "drop_off_type"}; + std::vector tripsHeader = {"route_id", "service_id", "trip_id", "trip_headsign", "trip_short_name", "direction_id", "block_id"}; std::vector transfersHeader = {"from_stop_id", "to_stop_id", "transfer_type", "min_transfer_time"}; - std::string gtfsDirectoryForAgency = dataDirectoryPath + "\\" + agencyName; + std::string gtfsDirectoryForDataSubset = dataDirectoryToWriteFilesTo + "\\" + agencyName; // create a subfolder for the agency - if (false == std::filesystem::create_directory(gtfsDirectoryForAgency)) + if (false == std::filesystem::create_directory(gtfsDirectoryForDataSubset)) { - getLogger(Target::CONSOLE, LoggerName::GTFS)->info("Error creating directory: " + dataDirectoryPath + agencyName + " ,it may already exists"); + getLogger(Target::CONSOLE, LoggerName::GTFS)->info("Could not create directory: " + gtfsDirectoryForDataSubset + " ,it may already exists"); } // write gtfs files in parallel std::vector> futures; // Write routes futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "routes.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "routes.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(routesHeader); for (const auto& route : routes) @@ -188,15 +196,17 @@ int main(int argc, char* argv[]) { row.push_back(quote(route.agencyId)); row.push_back(quote(route.routeShortName)); row.push_back(quote(route.routeLongName)); + row.push_back(quote(std::string{})); // TODO use this field for future use row.push_back(quote(static_cast(route.routeType))); writer.write_row(row); } + file.close(); })); // Write trips futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "trips.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "trips.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(tripsHeader); for (const auto& trip : trips) @@ -208,13 +218,15 @@ int main(int argc, char* argv[]) { row.push_back(quote("")); row.push_back(quote("")); row.push_back(quote("")); + row.push_back(quote("")); writer.write_row(row); } + file.close(); })); // Write stopTimes futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "stop_times.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "stop_times.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(stopTimesHeader); for (const auto& stopTime : stopTimes) @@ -225,14 +237,17 @@ int main(int argc, char* argv[]) { row.push_back(quote(stopTime.departureTime.toString())); row.push_back(quote(stopTime.stopId)); row.push_back(quote(std::to_string(stopTime.stopSequence))); + row.push_back(quote("")); + row.push_back(quote("")); writer.write_row(row); } + file.close(); })); // write stops futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "stops.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "stops.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(stopsHeader); for (const auto& stop : stops) @@ -248,6 +263,7 @@ int main(int argc, char* argv[]) { writer.write_row(row); } + file.close(); })); auto formatDate = [](const std::chrono::year_month_day& ymd) { @@ -265,7 +281,7 @@ int main(int argc, char* argv[]) { // Write calendar futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "calendar.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "calendar.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(calendarHeader); for (const auto& calendar : calendars) @@ -284,11 +300,12 @@ int main(int argc, char* argv[]) { writer.write_row(row); } + file.close(); })); // Write calendar dates futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "calendar_dates.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "calendar_dates.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(calendarDatesHeader); for (const auto& calendarDate : calendarDates) @@ -301,11 +318,12 @@ int main(int argc, char* argv[]) { writer.write_row(row); } + file.close(); })); // Write transfer items futures.emplace_back(std::async(std::launch::async, [&]() { - std::ofstream file(gtfsDirectoryForAgency + "\\" + "transfers.txt", std::ios::binary); + std::ofstream file(gtfsDirectoryForDataSubset + "\\" + "transfers.txt", std::ios::binary); csv2::Writer> writer(file); writer.write_row(transfersHeader); for (const auto& transfer : transferItems) @@ -319,6 +337,7 @@ int main(int argc, char* argv[]) { writer.write_row(row); } + file.close(); })); for (auto& future : futures) @@ -326,7 +345,10 @@ int main(int argc, char* argv[]) { future.get(); } - getLogger(Target::CONSOLE, LoggerName::GTFS)->info("GTFS files written successfully"); + getConsoleLogger(LoggerName::GTFS)->info("GTFS files written successfully"); + + getConsoleLogger(LoggerName::GTFS)->info("GTFS files written to directory: " + gtfsDirectoryForDataSubset); + return EXIT_SUCCESS; diff --git a/logging/include/LoggerBridge.h b/logging/include/LoggerBridge.h index 01172cc6..de8935c2 100644 --- a/logging/include/LoggerBridge.h +++ b/logging/include/LoggerBridge.h @@ -25,4 +25,3 @@ class LOGGER_API LoggerBridge virtual void setLevel(Level level) = 0; [[nodiscard]] virtual Level getLevel() const = 0; }; - diff --git a/logging/include/LoggerFactory.h b/logging/include/LoggerFactory.h index 594a4aa9..7791227a 100644 --- a/logging/include/LoggerFactory.h +++ b/logging/include/LoggerFactory.h @@ -24,3 +24,5 @@ enum class LOGGER_API Target }; LOGGER_API std::shared_ptr getLogger(Target target, LoggerName name); + +LOGGER_API std::shared_ptr getConsoleLogger(LoggerName name); diff --git a/logging/src/LoggerBridgeImpl.cpp b/logging/src/LoggerBridgeImpl.cpp index b2c0e29a..c6e30521 100644 --- a/logging/src/LoggerBridgeImpl.cpp +++ b/logging/src/LoggerBridgeImpl.cpp @@ -4,6 +4,8 @@ #include "LoggerBridgeImpl.h" +#include + LoggerBridgeImpl::LoggerBridgeImpl(std::shared_ptr logger) : logger(std::move(logger)) {} @@ -29,13 +31,13 @@ void LoggerBridgeImpl::setLevel(const Level level) { { case INFO: spdLevel = spdlog::level::info; - break; + break; case WARN: spdLevel = spdlog::level::warn; - break; + break; case ERROR: spdLevel = spdlog::level::err; - break; + break; case DEBUG: [[fallthrough]]; default: @@ -58,4 +60,4 @@ LoggerBridge::Level LoggerBridgeImpl::getLevel() const { default: return DEBUG; } -} \ No newline at end of file +} diff --git a/logging/src/LoggerBridgeImpl.h b/logging/src/LoggerBridgeImpl.h index 7833e6e6..48ab692f 100644 --- a/logging/src/LoggerBridgeImpl.h +++ b/logging/src/LoggerBridgeImpl.h @@ -22,7 +22,7 @@ class LoggerBridgeImpl final : public LoggerBridge [[nodiscard]] Level getLevel() const override; - private: + std::shared_ptr logger; -}; \ No newline at end of file +}; diff --git a/logging/src/LoggerFactory.cpp b/logging/src/LoggerFactory.cpp index 9c205842..ae6dccd5 100644 --- a/logging/src/LoggerFactory.cpp +++ b/logging/src/LoggerFactory.cpp @@ -18,3 +18,7 @@ std::shared_ptr getLogger(Target const target, LoggerName const na return LoggingPool::getInstance().getLogger(target, loggerNames[name]); } + +std::shared_ptr getConsoleLogger(const LoggerName name) { + return getLogger(Target::CONSOLE, name); +} diff --git a/raptor/CMakeLists.txt b/raptor/CMakeLists.txt index a8fbc8ea..482523d4 100644 --- a/raptor/CMakeLists.txt +++ b/raptor/CMakeLists.txt @@ -10,31 +10,67 @@ target_sources(${PROJECT_NAME} PRIVATE src/Leg.h src/LegImpl.cpp src/LegImpl.h - include/IRaptorAlgorithmStrategy.h - include/IRaptorAlgorithmFactory.h src/RaptorAlgorithmFactory.cpp src/RaptorAlgorithmFactory.h src/strategies/RaptorStrategy.cpp src/strategies/RaptorStrategy.h + src/ConnectionImpl.cpp + src/ConnectionImpl.h + src/strategies/StandardRaptorStrategy.cpp + src/strategies/StandardRaptorStrategy.h + src/utils/RaptorData.cpp + src/utils/RaptorData.h + src/utils/RaptorDataBuilder.cpp + src/utils/RaptorDataBuilder.h + src/utils/RouteBuilder.cpp + src/RaptorRouter.cpp + src/RaptorRouter.h + src/Query.cpp + src/Query.h + src/utils/StopLabelsAndTimes.cpp + src/utils/StopLabelsAndTimes.h + src/FootpathRelaxer.cpp + src/FootpathRelaxer.h + src/RouteScanner.cpp + src/RouteScanner.h + src/QueryConfig.cpp + src/data/ActiveTrip.h + src/data/raptorRouteStructures.h + src/utils/RouteContainer.cpp + src/utils/RouteContainer.h + src/LabelPostprocessor.cpp + src/LabelPostprocessor.h + src/RaptorConnection.cpp + src/RaptorConnection.h + src/utils/LocalDateTime.cpp + src/utils/helperFunctions.h + PUBLIC + include/RaptorAlgorithm.h + include/config/QueryConfig.h include/raptorTypes.h include/IConnection.h include/IRaptor.h - src/ConnectionImpl.cpp - src/ConnectionImpl.h - src/utils/Label.h - src/utils/IndexedVisitedHashMap.cpp - src/utils/IndexedVisitedHashMap.h - src/utils/IndexedVisitedSet.cpp - src/utils/IndexedVisitedSet.h + include/IRaptorAlgorithmStrategy.h + include/IRaptorAlgorithmFactory.h + include/config/WalkingConfig.h + include/config/TransferConfig.h + include/usingTypes.h + include/config/TravelConfig.h + include/RouteBuilder.h + include/LocalDateTime.h + include/Connection.h ) -target_include_directories(${PROJECT_NAME} PUBLIC +target_include_directories(${PROJECT_NAME} + PUBLIC ${CMAKE_SOURCE_DIR}/logging/include ${CMAKE_SOURCE_DIR}/logging/src ${CMAKE_SOURCE_DIR}/schedule/src ${CMAKE_SOURCE_DIR}/schedule/include ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/config ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/data ) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/raptor/include/Connection.h b/raptor/include/Connection.h new file mode 100644 index 00000000..46cb384a --- /dev/null +++ b/raptor/include/Connection.h @@ -0,0 +1,40 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#pragma once + +#include "usingTypes.h" + + +#include + +namespace raptor { + class Leg; + + class Connection + { + public: + virtual ~Connection() = default; + + [[nodiscard]] virtual int getDepartureTime() const = 0; + + [[nodiscard]] virtual int getArrivalTime() const = 0; + + [[nodiscard]] virtual std::string getFromStopId() const = 0; + + [[nodiscard]] virtual std::string getToStopId() const = 0; + + [[nodiscard]] virtual int getDurationInSeconds() const = 0; + + [[nodiscard]] virtual std::vector> getWalkTransfers() const = 0; + + [[nodiscard]] virtual std::vector> getRouteLegs() const = 0; + + [[nodiscard]] virtual types::raptorInt getNumberOfSameStopTransfers() const = 0; + + [[nodiscard]] virtual types::raptorInt getNumberOfTotalTransfers() const = 0; + + [[nodiscard]] virtual std::vector> getLegs() const = 0; + }; +} diff --git a/raptor/include/IRaptorAlgorithmFactory.h b/raptor/include/IRaptorAlgorithmFactory.h index bf79cb99..eb68d73e 100644 --- a/raptor/include/IRaptorAlgorithmFactory.h +++ b/raptor/include/IRaptorAlgorithmFactory.h @@ -1,34 +1,34 @@ +// // +// // Created by MichaelBrunner on 27/06/2024. +// // // -// Created by MichaelBrunner on 27/06/2024. -// - -#ifndef IRAPTORALGORITHMFACTORY_H -#define IRAPTORALGORITHMFACTORY_H - -#include "gtfs/RelationManager.h" - - -#include -#include - -namespace raptor::strategy { - class IRaptorAlgorithmStrategy; -} - -namespace raptor::strategy::factory { - - class RAPTOR_API IRaptorAlgorithmFactory - { - public: - enum AlgorithmType - { - RAPTOR - }; - - virtual ~IRaptorAlgorithmFactory() = default; - - virtual std::unique_ptr create(AlgorithmType type, schedule::gtfs::RelationManager&& relationManager) = 0; - }; -} - -#endif //IRAPTORALGORITHMFACTORY_H +// #ifndef IRAPTORALGORITHMFACTORY_H +// #define IRAPTORALGORITHMFACTORY_H +// +// #include "TimetableManager.h" +// +// +// #include +// #include +// +// namespace raptor::strategy { +// class IRaptorAlgorithmStrategy; +// } +// +// namespace raptor::strategy::factory { +// +// class RAPTOR_API IRaptorAlgorithmFactory +// { +// public: +// enum AlgorithmType +// { +// RAPTOR +// }; +// +// virtual ~IRaptorAlgorithmFactory() = default; +// +// virtual std::unique_ptr create(AlgorithmType type, schedule::gtfs::TimetableManager&& relationManager) = 0; +// }; +// } +// +// #endif //IRAPTORALGORITHMFACTORY_H diff --git a/raptor/include/IRaptorAlgorithmStrategy.h b/raptor/include/IRaptorAlgorithmStrategy.h index 917639ff..bd73a818 100644 --- a/raptor/include/IRaptorAlgorithmStrategy.h +++ b/raptor/include/IRaptorAlgorithmStrategy.h @@ -18,7 +18,8 @@ namespace raptor::strategy { public: virtual ~IRaptorAlgorithmStrategy() = default; - virtual std::shared_ptr execute(utils::ConnectionRequest const& request) = 0; + // virtual std::shared_ptr execute(utils::ConnectionRequest const& request) = 0; + virtual std::shared_ptr execute(const std::string& sourceStop, const std::string& targetStop, unsigned int departureTime) = 0; }; } diff --git a/raptor/include/LocalDateTime.h b/raptor/include/LocalDateTime.h new file mode 100644 index 00000000..ef3c9887 --- /dev/null +++ b/raptor/include/LocalDateTime.h @@ -0,0 +1,86 @@ +// +// Created by MichaelBrunner on 04/08/2024. +// + +// TODO create a Utils library?? + +#pragma once + +#include +#include + +using namespace std::chrono_literals; + +namespace raptor::utils { + + class RAPTOR_API LocalDateTime + { + std::chrono::local_time dateTime; + + public: + LocalDateTime(const std::chrono::year year, const std::chrono::month month, const std::chrono::day day, const std::chrono::hours hour, const std::chrono::minutes minute, const std::chrono::seconds second) { + using namespace std::chrono; + dateTime = local_days{year / month / day} + hour + minute + second; + } + + LocalDateTime() = default; + + [[nodiscard]] long long secondsOfDay() const { + using namespace std::chrono; + const auto time_since_epoch = dateTime.time_since_epoch(); + const auto days_since_epoch = duration_cast(time_since_epoch); + const auto time_of_day = time_since_epoch - days_since_epoch; + return duration_cast(time_of_day).count(); + } + + [[nodiscard]] long long getMinutes() const { + return secondsOfDay() % 3600 / 60; + } + + [[nodiscard]] long long getHours() const { + return secondsOfDay() / 3600; + } + + [[nodiscard]] long long getSeconds() const { + return secondsOfDay() % 60; + } + + [[nodiscard]] LocalDateTime addHours(const std::chrono::hours hours) const { + using namespace std::chrono; + const auto new_dateTime = dateTime + hours; + const auto time_since_epoch = new_dateTime.time_since_epoch(); + const auto days_since_epoch = duration_cast(time_since_epoch); + const auto time_of_day = time_since_epoch - days_since_epoch; + const auto new_hours = duration_cast(time_of_day); + const auto new_minutes = duration_cast(time_of_day % std::chrono::hours(1)); + const auto new_seconds = duration_cast(time_of_day % minutes(1)); + return LocalDateTime{year{1970}, month{1}, day{1}, new_hours, new_minutes, new_seconds}; + } + }; + + inline LocalDateTime fromSecondsOfDay(const long long total_seconds, const std::chrono::year year, const std::chrono::month month, const std::chrono::day day) { + using namespace std::chrono; + const auto hours = total_seconds / 3600; + const auto minutes = (total_seconds % 3600) / 60; + const auto seconds = total_seconds % 60; + return LocalDateTime{year, month, day, std::chrono::hours(hours), std::chrono::minutes(minutes), std::chrono::seconds(seconds)}; + } + + inline LocalDateTime fromSecondsOfToday(const long long total_seconds) { + using namespace std::chrono; + const auto hours = total_seconds / 3600; + const auto minutes = (total_seconds % 3600) / 60; + const auto seconds = total_seconds % 60; + + const auto now = system_clock::now(); + + const auto nowLocal = zoned_time{current_zone(), now}.get_local_time(); + // Convert to year_month_day + const auto yearMonthDay = year_month_day{floor(nowLocal)}; + + return LocalDateTime{yearMonthDay.year(), yearMonthDay.month(), yearMonthDay.day(), std::chrono::hours(hours), std::chrono::minutes(minutes), std::chrono::seconds(seconds)}; + } + + +} // utils +// raptor diff --git a/raptor/include/RaptorAlgorithm.h b/raptor/include/RaptorAlgorithm.h new file mode 100644 index 00000000..cc4bbb29 --- /dev/null +++ b/raptor/include/RaptorAlgorithm.h @@ -0,0 +1,53 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#ifndef RAPTOR_ALGORITHM_H +#define RAPTOR_ALGORITHM_H + +#include "Connection.h" + + +#include +#include +#include +#include +#include +#include + +namespace raptor { + + + class RAPTOR_API RaptorAlgorithm + { + public: + // Destructor + virtual ~RaptorAlgorithm() = default; + + // Method to route the earliest arrival + [[nodiscard]] virtual std::vector> routeEarliestArrival( + const std::map& departureStops, + const std::map& arrivalStops, + const config::QueryConfig& config) const + = 0; + + // Method to route latest departure + [[nodiscard]] virtual std::vector> routeLatestDeparture( + const std::map& departureStops, + const std::map& arrivalStops, + const config::QueryConfig& config) const + = 0; + + // Method to route isolines + [[nodiscard]] virtual std::map> routeIsolines( + const std::map& sourceStops, + const config::QueryConfig& config) const + = 0; + + protected: + RaptorAlgorithm() = default; + }; + +} + +#endif // RAPTOR_ALGORITHM_H diff --git a/raptor/include/RouteBuilder.h b/raptor/include/RouteBuilder.h new file mode 100644 index 00000000..bafe2aef --- /dev/null +++ b/raptor/include/RouteBuilder.h @@ -0,0 +1,36 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +// RouteBuilder.h +#pragma once + +#include +#include +#include +#include +#include "utils/RouteContainer.h" + +#include + + +namespace raptor { + + class RAPTOR_API RouteBuilder + { + public: + RouteBuilder(const std::string& routeId, const std::vector& stopIds); + + void addTrip(const std::string& tripId); + void addStopTime(const std::string& tripId, int position, const std::string& stopId, const StopTime& stopTime); + RouteContainer build(); + + private: + void validate() const; + + std::string routeId; + std::map stopSequence; + std::unordered_map> trips; + }; + +} // namespace raptor diff --git a/raptor/include/config/QueryConfig.h b/raptor/include/config/QueryConfig.h new file mode 100644 index 00000000..0a42d4b9 --- /dev/null +++ b/raptor/include/config/QueryConfig.h @@ -0,0 +1,41 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#pragma once +#include "TravelConfig.h" +#include "usingTypes.h" + + +#include +#include +#include + +namespace raptor::config { + class RAPTOR_API QueryConfig + { + public: + QueryConfig(); + + // QueryConfig(int maxWalkingDuration, int minTransferDuration, int maxTransferNumber, int maxTravelTime); + + explicit QueryConfig(const WalkingConfig& walkingConfig, const TransferConfig& transferConfig, const TravelConfig& travelConfig); + + [[nodiscard]] auto getMaximumWalkingDuration() const -> types::raptorInt; + [[nodiscard]] auto getMinimumTransferDuration() const -> types::raptorInt; + [[nodiscard]] auto getMaximumTransferNumber() const -> types::raptorInt; + [[nodiscard]] auto getMaximumTravelTime() const -> types::raptorInt; + + void setMaximumWalkingDuration(types::raptorInt maxWalkingDuration); + void setMinimumTransferDuration(types::raptorInt minTransferDuration); + void setMaximumTransferNumber(types::raptorInt maxTransferNumber); + void setMaximumTravelTime(types::raptorInt maxTravelTime); + + private: + types::raptorInt maximumWalkingDuration; + types::raptorInt minimumTransferDuration; + types::raptorInt maximumTransferNumber; + types::raptorInt maximumTravelTime; + }; + +} \ No newline at end of file diff --git a/raptor/include/config/TransferConfig.h b/raptor/include/config/TransferConfig.h new file mode 100644 index 00000000..320c456a --- /dev/null +++ b/raptor/include/config/TransferConfig.h @@ -0,0 +1,33 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +namespace raptor::config { + class TransferConfig + { + public: + explicit TransferConfig(int const minTransferDuration, int const maxTransferNumber) + : minTransferDuration(minTransferDuration) + , maxTransferNumber(maxTransferNumber) { + } + + [[nodiscard]] int getMinTransferDuration() const { + return minTransferDuration; + } + [[nodiscard]] int getMaxTransferNumber() const { + return maxTransferNumber; + } + void setMinTransferDuration(int const minTransferDuration) { + this->minTransferDuration = minTransferDuration; + } + void setMaxTransferNumber(int const maxTransferNumber) { + this->maxTransferNumber = maxTransferNumber; + } + + private: + int minTransferDuration; + int maxTransferNumber; + }; +} diff --git a/raptor/include/config/TravelConfig.h b/raptor/include/config/TravelConfig.h new file mode 100644 index 00000000..8ad55ac4 --- /dev/null +++ b/raptor/include/config/TravelConfig.h @@ -0,0 +1,30 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +namespace raptor::config { + class TravelConfig + { + public: + explicit TravelConfig(int const maxTravelTime) + : maximumTravelTime(maxTravelTime) { + } + + [[nodiscard]] int getMaxTravelTime() const { + return maximumTravelTime; + } + + void setMaxTravelTime(int const maxTravelTime) { + this->maximumTravelTime = maxTravelTime; + } + + int operator()() const { + return maximumTravelTime; + } + + private: + int maximumTravelTime; + }; +} diff --git a/raptor/include/config/WalkingConfig.h b/raptor/include/config/WalkingConfig.h new file mode 100644 index 00000000..0d7f2c84 --- /dev/null +++ b/raptor/include/config/WalkingConfig.h @@ -0,0 +1,30 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +namespace raptor::config { + class WalkingConfig + { + public: + explicit WalkingConfig(int const maxWalkingDuration) + : maxWalkingDuration(maxWalkingDuration) { + } + + [[nodiscard]] int getMaxWalkingDuration() const { + return maxWalkingDuration; + } + + void setMaxWalkingDuration(int const maxWalkingDuration) { + this->maxWalkingDuration = maxWalkingDuration; + } + + int operator()() const { + return maxWalkingDuration; + } + + private: + int maxWalkingDuration; + }; +} diff --git a/raptor/include/raptorTypes.h b/raptor/include/raptorTypes.h index 272a33b5..25167e45 100644 --- a/raptor/include/raptorTypes.h +++ b/raptor/include/raptorTypes.h @@ -5,23 +5,23 @@ #ifndef RAPTOR_TYPES_H #define RAPTOR_TYPES_H -#include +#include #include namespace raptor::utils { - static constexpr auto INFINITY_VALUE = std::numeric_limits::max(); + static constexpr auto INFINITY_VALUE = std::numeric_limits::max(); - using stopId = std::string; + using stop_id = std::string; + using route_id = std::string; struct ConnectionRequest { explicit ConnectionRequest(std::vector&& departureStopId, std::vector&& arrivalStopId, const unsigned int earliestDepartureTime) - : departureStopId(std::move(departureStopId)), - arrivalStopId(std::move(arrivalStopId)), - earliestDepartureTime(earliestDepartureTime) - { + : departureStopId(std::move(departureStopId)) + , arrivalStopId(std::move(arrivalStopId)) + , earliestDepartureTime(earliestDepartureTime) { latestDepartureTime = earliestDepartureTime + 3'600; } std::vector departureStopId{}; diff --git a/raptor/include/usingTypes.h b/raptor/include/usingTypes.h new file mode 100644 index 00000000..c6f0893c --- /dev/null +++ b/raptor/include/usingTypes.h @@ -0,0 +1,16 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once +#include + +namespace raptor::types { + static constexpr auto INFINITY_VALUE_MAX = std::numeric_limits::max(); + static constexpr auto INFINITY_VALUE_MIN = std::numeric_limits::min(); + + static constexpr int NO_INDEX = -1; + + using raptorInt = unsigned int; + using raptorIdx = unsigned int; +} \ No newline at end of file diff --git a/raptor/src/FootpathRelaxer.cpp b/raptor/src/FootpathRelaxer.cpp new file mode 100644 index 00000000..4b032da9 --- /dev/null +++ b/raptor/src/FootpathRelaxer.cpp @@ -0,0 +1,100 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#include "FootpathRelaxer.h" + +#include "LoggerFactory.h" +#include "usingTypes.h" + +#include +#include + +namespace raptor { + + FootpathRelaxer::FootpathRelaxer(StopLabelsAndTimes& stopLabelsAndTimes, const RaptorData& raptorData, const types::raptorInt minimumTransferDuration, const types::raptorInt maximumWalkingDuration) + : transfers(raptorData.getStopContext().transfers) + , stops(raptorData.getStopContext().stops) + , minTransferDuration(minimumTransferDuration) + , maxWalkingDuration(maximumWalkingDuration) + , stopLabelsAndTimes(stopLabelsAndTimes) { + } + + std::unordered_set FootpathRelaxer::relaxInitial(const std::vector& stopIndices) const { + + std::unordered_set newlyMarkedStops; + + getConsoleLogger(LoggerName::RAPTOR)->info("Initial relaxing of footpaths for source stops"); + + for (const auto sourceStopIdx : stopIndices) + { + expandFootpathsFromStop(sourceStopIdx, 0, newlyMarkedStops); + } + + return newlyMarkedStops; + } + + std::unordered_set FootpathRelaxer::relax(const types::raptorInt round, const std::unordered_set& stopIndices) const { + std::unordered_set newlyMarkedStops; + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Relaxing footpaths for round {}", round)); + + for (const auto sourceStopIdx : stopIndices) + { + expandFootpathsFromStop(sourceStopIdx, round, newlyMarkedStops); + } + + return newlyMarkedStops; + } + + void FootpathRelaxer::expandFootpathsFromStop(const types::raptorIdx stopIdx, const types::raptorInt round, std::unordered_set& markedStops) const { + if (stops[stopIdx].numberOfTransfers == 0) + { + return; + } + + const auto& stop = stops[stopIdx]; + const auto previousLabel = stopLabelsAndTimes.getLabel(round, stopIdx); + + if (previousLabel == nullptr || previousLabel->type == StopLabelsAndTimes::LabelType::TRANSFER) + { + return; + } + + auto sourceTime = previousLabel->targetTime; + + for (auto i = stop.transferIndex; i < stop.transferIndex + static_cast(stop.numberOfTransfers); ++i) + { + const auto& transfer = transfers[i]; + const auto& targetStop = stops[transfer.targetStopIndex]; + + if (maxWalkingDuration < transfer.duration) + { + continue; + } + + auto targetTime = sourceTime + transfer.duration + minTransferDuration; + const auto comparableTargetTime = targetTime - targetStop.sameStopTransferTime; + + // if label is not improved, continue + const auto bestTimeToCompareTo = stopLabelsAndTimes.getComparableBestTime(transfer.targetStopIndex); + if (comparableTargetTime >= bestTimeToCompareTo) + { + continue; + } + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} was improved by transfer from stop {}", stop.id, targetStop.id)); + + stopLabelsAndTimes.setBestTime(transfer.targetStopIndex, comparableTargetTime); + + auto label = std::make_unique(sourceTime, + targetTime, + StopLabelsAndTimes::LabelType::TRANSFER, + i, + types::NO_INDEX, + transfer.targetStopIndex, + stopLabelsAndTimes.getLabel(round, stopIdx)); + + stopLabelsAndTimes.setLabel(round, transfer.targetStopIndex, std::move(label)); + markedStops.insert(transfer.targetStopIndex); + } + } +} // raptor \ No newline at end of file diff --git a/raptor/src/FootpathRelaxer.h b/raptor/src/FootpathRelaxer.h new file mode 100644 index 00000000..f8863236 --- /dev/null +++ b/raptor/src/FootpathRelaxer.h @@ -0,0 +1,38 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#ifndef FOOTPATHRELAXER_H +#define FOOTPATHRELAXER_H + +#include "utils/StopLabelsAndTimes.h" +#include "usingTypes.h" +#include "utils/RaptorData.h" + +#include +#include + +namespace raptor { + + class FootpathRelaxer + { + public: + FootpathRelaxer(StopLabelsAndTimes& stopLabelsAndTimes, const RaptorData& raptorData, types::raptorInt minimumTransferDuration, types::raptorInt maximumWalkingDuration); + + [[nodiscard]] std::unordered_set relaxInitial(const std::vector& stopIndices) const; + + [[nodiscard]] std::unordered_set relax(types::raptorInt round, const std::unordered_set& stopIndices) const; + + private: + void expandFootpathsFromStop(types::raptorIdx stopIdx, types::raptorInt round, std::unordered_set& markedStops) const; + + const std::vector& transfers; + const std::vector& stops; + const types::raptorInt minTransferDuration; + const types::raptorInt maxWalkingDuration; + StopLabelsAndTimes& stopLabelsAndTimes; + }; + +} // raptor + +#endif //FOOTPATHRELAXER_H diff --git a/raptor/src/LabelPostprocessor.cpp b/raptor/src/LabelPostprocessor.cpp new file mode 100644 index 00000000..dddb31bd --- /dev/null +++ b/raptor/src/LabelPostprocessor.cpp @@ -0,0 +1,123 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#include "LabelPostprocessor.h" + +#include "LegImpl.h" +#include "RaptorConnection.h" +#include "RaptorRouter.h" +#include "RouteScanner.h" + +#include +#include +#include + +namespace raptor { + + + LabelPostprocessor::LabelPostprocessor(const RaptorRouter& raptorData) + : stops(raptorData.getRaptorData().getStopContext().stops) + , stopTimes(raptorData.getRaptorData().getRouteTraversal().stopTimes) + , routes(raptorData.getRaptorData().getRouteTraversal().routes) + , routeStops(raptorData.getRaptorData().getRouteTraversal().routeStops) { + } + + + std::vector> LabelPostprocessor::reconstructParetoOptimalSolutions(const std::vector>>& bestLabelsPerRound, + const std::map& targetStops, + const types::raptorInt& referenceDate) const { + std::vector> connections{}; + for (const auto& labels : bestLabelsPerRound) + { + const StopLabelsAndTimes::Label* bestLabel = nullptr; + auto bestTime = types::INFINITY_VALUE_MAX; + + for (const auto& [targetStopIdx, targetStopWalkingTime] : targetStops) + { + const auto currentLabel = labels[targetStopIdx].get(); + if (nullptr == currentLabel) + { + continue; + } + + const auto actualArrivalTime = currentLabel->targetTime + targetStopWalkingTime; + if (actualArrivalTime < bestTime) + { + bestLabel = currentLabel; + bestTime = actualArrivalTime; + } + } + + if (nullptr == bestLabel) + { + continue; + } + + if (auto connection = reconstructConnectionFromLabel(bestLabel, referenceDate)) + { + connections.push_back(std::move(connection)); + } + } + return connections; + } + + std::unique_ptr LabelPostprocessor::reconstructConnectionFromLabel(const StopLabelsAndTimes::Label* label, const types::raptorInt& referenceDate) const { + + auto connection = std::make_unique(); + std::vector labels; + + while (label->type != StopLabelsAndTimes::LabelType::INITIAL) + { + assert(label->previous); + labels.push_back(label); + label = label->previous; + } + + for (const auto& currentLabel : labels) + { + std::string routeId; + std::string tripId; + std::string fromStopId; + std::string toStopId; + Leg::Type type; + + assert(currentLabel->previous != nullptr); + + fromStopId = stops[currentLabel->previous->stopIdx].id; + toStopId = stops[currentLabel->stopIdx].id; + + + const types::raptorIdx departureTimestamp = currentLabel->sourceTime; + const types::raptorIdx arrivalTimestamp = currentLabel->targetTime; + + if (currentLabel->type == StopLabelsAndTimes::LabelType::ROUTE) + { + const auto& route = routes[currentLabel->routeOrTransferIdx]; + tripId = route.tripIds[currentLabel->tripOffset]; + routeId = route.id; + type = Leg::Type::ROUTE; + } + else if (currentLabel->type == StopLabelsAndTimes::LabelType::TRANSFER) + { + routeId = std::format("transfer_{}_{}", fromStopId, toStopId); + type = Leg::Type::WALK_TRANSFER; + } + else + { + throw std::runtime_error("Unknown label type"); + } + + connection->addLeg(std::make_shared(routeId, tripId, fromStopId, toStopId, departureTimestamp, arrivalTimestamp, type)); + } + + if (!connection->getLegs().empty()) + { + connection->initialize(); + return connection; + } + return nullptr; + } + + +} // raptor \ No newline at end of file diff --git a/raptor/src/LabelPostprocessor.h b/raptor/src/LabelPostprocessor.h new file mode 100644 index 00000000..81e7989e --- /dev/null +++ b/raptor/src/LabelPostprocessor.h @@ -0,0 +1,35 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#pragma once + +#include "Connection.h" +#include "RaptorConnection.h" +#include "raptorRouteStructures.h" +#include "usingTypes.h" +#include "utils/StopLabelsAndTimes.h" + +#include +#include + +namespace raptor { + class RaptorRouter; + + class LabelPostprocessor + { + std::vector stops; + std::vector stopTimes; + std::vector routes; + std::vector routeStops; + + std::unique_ptr reconstructConnectionFromLabel(const StopLabelsAndTimes::Label* label, const types::raptorInt& referenceDate) const; + + public: + explicit LabelPostprocessor(const RaptorRouter& raptorData); + std::vector> reconstructParetoOptimalSolutions(const std::vector>>& bestLabelsPerRound, + const std::map& targetStops, + const types::raptorInt& referenceDate) const; + }; + +} // raptor diff --git a/raptor/src/Leg.h b/raptor/src/Leg.h index 9469aad6..8553ac87 100644 --- a/raptor/src/Leg.h +++ b/raptor/src/Leg.h @@ -33,6 +33,14 @@ namespace raptor { [[nodiscard]] virtual int getArrivalTime() const = 0; [[nodiscard]] virtual std::optional getType() const = 0; + + bool operator<(const Leg& other) const { + if (getDepartureTime() != other.getDepartureTime()) + { + return getDepartureTime() < other.getDepartureTime(); + } + return getArrivalTime() < other.getArrivalTime(); + } }; } diff --git a/raptor/src/LegImpl.cpp b/raptor/src/LegImpl.cpp index 6c551921..41385879 100644 --- a/raptor/src/LegImpl.cpp +++ b/raptor/src/LegImpl.cpp @@ -6,7 +6,7 @@ namespace raptor { - LegImpl::LegImpl(std::string routeId, std::string tripId, std::string fromStopId, std::string toStopId, const int departureTime, const int arrivalTime, Type type) + LegImpl::LegImpl(std::string routeId, std::string tripId, std::string fromStopId, std::string toStopId, const types::raptorIdx departureTime, const types::raptorIdx arrivalTime, Type type) : routeId(std::move(routeId)) , tripId(std::move(tripId)) , fromStopId(std::move(fromStopId)) diff --git a/raptor/src/LegImpl.h b/raptor/src/LegImpl.h index a3725893..b2b31584 100644 --- a/raptor/src/LegImpl.h +++ b/raptor/src/LegImpl.h @@ -6,6 +6,8 @@ #define LEGIMPL_H #include "Leg.h" +#include "usingTypes.h" + #include namespace raptor { @@ -17,12 +19,12 @@ namespace raptor { std::string tripId; std::string fromStopId; std::string toStopId; - int departureTime = 0; - int arrivalTime = 0; + types::raptorIdx departureTime; + types::raptorIdx arrivalTime; std::optional type = std::nullopt; public: - explicit LegImpl(std::string routeId, std::string tripId, std::string fromStopId, std::string toStopId, int departureTime, int arrivalTime, Type type); + explicit LegImpl(std::string routeId, std::string tripId, std::string fromStopId, std::string toStopId, types::raptorIdx departureTime, types::raptorIdx arrivalTime, Type type); ~LegImpl() override = default; diff --git a/raptor/src/Query.cpp b/raptor/src/Query.cpp new file mode 100644 index 00000000..230d3fff --- /dev/null +++ b/raptor/src/Query.cpp @@ -0,0 +1,147 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#include "Query.h" + +#include "FootpathRelaxer.h" +#include "LoggerFactory.h" +#include "RouteScanner.h" + +#include +#include +#include + + +namespace raptor { + + + Query::Query(const QueryParams& params) + : sourceStopIndices(params.sourceStopIndices) + , targetStopIndices(params.targetStopIndices) + , sourceTimes(params.sourceTimes) + , walkingDurationsToTarget(params.walkingDurationsToTarget) + , raptorData(params.raptorData) + , config(params.config) + , stopLabelsAndTimes(static_cast(this->raptorData.getStopContext().stops.size())) { + + if (sourceStopIndices.size() != sourceTimes.size()) + { + throw std::invalid_argument("Source stops and departure/arrival times must have the same size."); + } + + if (targetStopIndices.size() != walkingDurationsToTarget.size()) + { + throw std::invalid_argument("Target stops and walking durations to target must have the same size."); + } + + targetStops.resize(targetStopIndices.size() * 2); + cutoffTime = determineCutoffTime(); + } + + const std::vector>>& Query::run() { + + const auto footpathRelaxer = FootpathRelaxer(stopLabelsAndTimes, raptorData, config.getMinimumTransferDuration(), config.getMaximumWalkingDuration()); + const auto routeScanner = RouteScanner(stopLabelsAndTimes, raptorData, config.getMinimumTransferDuration()); + + auto markedStops = initialize(); + auto newStops = footpathRelaxer.relaxInitial(sourceStopIndices); + markedStops.insert(newStops.begin(), newStops.end()); + markedStops = removeSuboptimalLabelsForRound(0, markedStops); + + types::raptorInt round = 1; + while (!markedStops.empty() && (round - 1) <= config.getMaximumTransferNumber()) + { + stopLabelsAndTimes.addNewRound(); + + auto markedStopsNext = routeScanner.scan(round, markedStops); + auto relaxedStops = footpathRelaxer.relax(round, markedStopsNext); + markedStopsNext.insert(relaxedStops.begin(), relaxedStops.end()); + + markedStops = removeSuboptimalLabelsForRound(round, markedStopsNext); + round++; + } + + return stopLabelsAndTimes.getBestLabelsPerRound(); + } + + std::unordered_set Query::initialize() { + getConsoleLogger(LoggerName::RAPTOR)->info("Initializing global best times per stop and best labels per round"); + + for (size_t i = 0; i < targetStopIndices.size(); ++i) + { + targetStops[i * 2] = targetStopIndices[i]; + targetStops[i * 2 + 1] = walkingDurationsToTarget[i]; + } + + std::unordered_set markedStops{}; + for (size_t i = 0; i < sourceStopIndices.size(); ++i) + { + auto currentStopIdx = sourceStopIndices[i]; + auto targetTime = sourceTimes[i]; + + auto label = std::make_unique(0, targetTime, StopLabelsAndTimes::LabelType::INITIAL, types::NO_INDEX, types::NO_INDEX, currentStopIdx, nullptr); + stopLabelsAndTimes.setLabel(0, currentStopIdx, std::move(label)); + stopLabelsAndTimes.setBestTime(currentStopIdx, targetTime); + + markedStops.insert(currentStopIdx); + } + + return markedStops; + } + + std::unordered_set Query::removeSuboptimalLabelsForRound(const types::raptorInt round, const std::unordered_set& markedStops) { + const auto bestTime = getBestTimeForAllTargetStops(); + if (bestTime == types::INFINITY_VALUE_MAX) + { + return markedStops; + } + + std::unordered_set markedStopsClean; + for (const auto stopIdx : markedStops) + { + if (const auto label = stopLabelsAndTimes.getLabel(round, stopIdx)) + { + if (label->targetTime > bestTime) + { + stopLabelsAndTimes.setLabel(round, stopIdx, nullptr); + } + else + { + markedStopsClean.insert(stopIdx); + } + } + } + + return markedStopsClean; + } + + types::raptorIdx Query::getBestTimeForAllTargetStops() const { + auto bestTime = cutoffTime; + + for (auto i = 0; i < targetStops.size(); i += 2) + { + const auto targetStopIdx = targetStops[i]; + const auto walkDurationToTarget = targetStops[i + 1]; + + if (auto bestTimeForStop = stopLabelsAndTimes.getActualBestTime(targetStopIdx); + bestTimeForStop != types::INFINITY_VALUE_MAX) + { + bestTimeForStop += walkDurationToTarget; + bestTime = std::min(bestTime, bestTimeForStop); + } + } + + return bestTime; + } + + types::raptorIdx Query::determineCutoffTime() const { + if (config.getMaximumTravelTime() == types::INFINITY_VALUE_MAX) + { + return types::INFINITY_VALUE_MAX; + } + const auto latestArrival = *std::ranges::max_element(sourceTimes); + return latestArrival - config.getMaximumTravelTime(); + } + +} // raptor \ No newline at end of file diff --git a/raptor/src/Query.h b/raptor/src/Query.h new file mode 100644 index 00000000..6238e11c --- /dev/null +++ b/raptor/src/Query.h @@ -0,0 +1,60 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#ifndef QUERY_H +#define QUERY_H + +#include "QueryConfig.h" +#include "utils/StopLabelsAndTimes.h" +#include "utils/RaptorData.h" + +#include +#include +#include +#include +#include +#include + +namespace raptor { + + struct QueryParams + { + const RaptorData& raptorData; + std::vector sourceStopIndices; + std::vector targetStopIndices; + std::vector sourceTimes; + std::vector walkingDurationsToTarget; + config::QueryConfig config; + }; + + class Query + { + public: + + explicit Query(const QueryParams& params); + + const std::vector>>& run(); + + private: + std::vector sourceStopIndices; + std::vector targetStopIndices; + std::vector sourceTimes; + std::vector walkingDurationsToTarget; + + RaptorData raptorData; + config::QueryConfig config; + + std::vectortargetStops; + types::raptorInt cutoffTime; + StopLabelsAndTimes stopLabelsAndTimes; + + std::unordered_set initialize(); + std::unordered_set removeSuboptimalLabelsForRound(types::raptorInt round, const std::unordered_set& markedStops); + [[nodiscard]] types::raptorInt getBestTimeForAllTargetStops() const; + [[nodiscard]] types::raptorInt determineCutoffTime() const; + }; + +} // raptor + +#endif //QUERY_H diff --git a/raptor/src/QueryConfig.cpp b/raptor/src/QueryConfig.cpp new file mode 100644 index 00000000..7032b444 --- /dev/null +++ b/raptor/src/QueryConfig.cpp @@ -0,0 +1,58 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#include "QueryConfig.h" + +#include "usingTypes.h" + +#include + +namespace raptor::config { + + QueryConfig::QueryConfig() + : maximumWalkingDuration(types::INFINITY_VALUE_MAX) + , minimumTransferDuration(0) + , maximumTransferNumber(types::INFINITY_VALUE_MAX) + , maximumTravelTime(types::INFINITY_VALUE_MAX) {} + + + QueryConfig::QueryConfig(const WalkingConfig& walkingConfig, const TransferConfig& transferConfig, const TravelConfig& travelConfig) + : maximumWalkingDuration(walkingConfig.getMaxWalkingDuration()) + , minimumTransferDuration(transferConfig.getMinTransferDuration()) + , maximumTransferNumber(transferConfig.getMaxTransferNumber()) + , maximumTravelTime(travelConfig.getMaxTravelTime()) { + } + + auto QueryConfig::getMaximumWalkingDuration() const -> types::raptorInt { + return maximumWalkingDuration; + } + + auto QueryConfig::getMinimumTransferDuration() const -> types::raptorInt { + return minimumTransferDuration; + } + + auto QueryConfig::getMaximumTransferNumber() const -> types::raptorInt { + return maximumTransferNumber; + } + + auto QueryConfig::getMaximumTravelTime() const -> types::raptorInt { + return maximumTravelTime; + } + + void QueryConfig::setMaximumWalkingDuration(const types::raptorInt maxWalkingDuration) { + maximumWalkingDuration = maxWalkingDuration; + } + + void QueryConfig::setMinimumTransferDuration(const types::raptorInt minTransferDuration) { + minimumTransferDuration = minTransferDuration; + } + + void QueryConfig::setMaximumTransferNumber(const types::raptorInt maxTransferNumber) { + maximumTransferNumber = maxTransferNumber; + } + + void QueryConfig::setMaximumTravelTime(const types::raptorInt maxTravelTime) { + maximumTravelTime = maxTravelTime; + } +} diff --git a/raptor/src/Raptor.cpp b/raptor/src/Raptor.cpp index 6c7a4f59..a5929e64 100644 --- a/raptor/src/Raptor.cpp +++ b/raptor/src/Raptor.cpp @@ -1,31 +1,31 @@ +// // +// // Created by MichaelBrunner on 23/06/2024. +// // // -// Created by MichaelBrunner on 23/06/2024. -// - -#include "Raptor.h" - -#include "LoggerFactory.h" - -#include -#include "raptorTypes.h" - - -namespace raptor { - - Raptor::Raptor(std::unique_ptr&& strategy) - : strategy(std::move(strategy)) { - if (!this->strategy) - { - throw std::invalid_argument("strategy must not be null"); - } - } - - std::shared_ptr Raptor::getConnections(utils::ConnectionRequest const& request) const { - return strategy->execute(request); - } - void Raptor::testFunction() { - getLogger(Target::CONSOLE, LoggerName::RAPTOR)->info("Test function called"); - } - - -} // raptor \ No newline at end of file +// #include "Raptor.h" +// +// #include "LoggerFactory.h" +// +// #include +// #include "raptorTypes.h" +// +// +// namespace raptor { +// +// Raptor::Raptor(std::unique_ptr&& strategy) +// : strategy(std::move(strategy)) { +// if (!this->strategy) +// { +// throw std::invalid_argument("strategy must not be null"); +// } +// } +// +// std::shared_ptr Raptor::getConnections(utils::ConnectionRequest const& request) const { +// return strategy->execute(request.departureStopId.front(), request.arrivalStopId.front(), request.earliestDepartureTime); +// } +// void Raptor::testFunction() { +// getLogger(Target::CONSOLE, LoggerName::RAPTOR)->info("Test function called"); +// } +// +// +// } // raptor \ No newline at end of file diff --git a/raptor/src/Raptor.h b/raptor/src/Raptor.h index f9cd23d3..6d8543e7 100644 --- a/raptor/src/Raptor.h +++ b/raptor/src/Raptor.h @@ -1,3 +1,4 @@ +/* // // Created by MichaelBrunner on 23/06/2024. // @@ -9,7 +10,7 @@ #include -#include +#include #include #include #include @@ -34,3 +35,4 @@ namespace raptor { } // raptor #endif //RAPTOR_H +*/ diff --git a/raptor/src/RaptorAlgorithmFactory.cpp b/raptor/src/RaptorAlgorithmFactory.cpp index 077e36c4..263ac8a4 100644 --- a/raptor/src/RaptorAlgorithmFactory.cpp +++ b/raptor/src/RaptorAlgorithmFactory.cpp @@ -1,16 +1,16 @@ +// // +// // Created by MichaelBrunner on 27/06/2024. +// // // -// Created by MichaelBrunner on 27/06/2024. +// #include "RaptorAlgorithmFactory.h" // - -#include "RaptorAlgorithmFactory.h" - - -namespace raptor::strategy::factory { - - std::unique_ptr RaptorAlgorithmFactory::create(const AlgorithmType type, schedule::gtfs::RelationManager&& relationManager) { - return std::move(strategies[type](std::move(relationManager))); - } -} // factory - // strategy - // gtfs - // raptor \ No newline at end of file +// +// namespace raptor::strategy::factory { +// +// std::unique_ptr RaptorAlgorithmFactory::create(const AlgorithmType type, schedule::gtfs::TimetableManager&& relationManager) { +// return std::move(strategies[type](std::move(relationManager))); +// } +// } // factory +// // strategy +// // gtfs +// // raptor \ No newline at end of file diff --git a/raptor/src/RaptorAlgorithmFactory.h b/raptor/src/RaptorAlgorithmFactory.h index 5d9249c6..ed001268 100644 --- a/raptor/src/RaptorAlgorithmFactory.h +++ b/raptor/src/RaptorAlgorithmFactory.h @@ -1,32 +1,32 @@ +// // +// // Created by MichaelBrunner on 27/06/2024. +// // // -// Created by MichaelBrunner on 27/06/2024. -// - -#ifndef RAPTORALGORITHMFACTORY_H -#define RAPTORALGORITHMFACTORY_H - -#include "IRaptorAlgorithmFactory.h" -#include "strategies/RaptorStrategy.h" - -#include -#include -#include - -namespace raptor::strategy::factory { - - class RAPTOR_API RaptorAlgorithmFactory final : public IRaptorAlgorithmFactory - { - public: - std::unique_ptr create(AlgorithmType type, schedule::gtfs::RelationManager&& relationManager) override; - - private: - std::map(schedule::gtfs::RelationManager&& relationManager)>> strategies{ - {RAPTOR, [](schedule::gtfs::RelationManager&& relationManager) { return std::make_unique(std::move(relationManager)); }}}; - }; - -} // factory -// strategy -// gtfs -// raptor - -#endif //RAPTORALGORITHMFACTORY_H +// #ifndef RAPTORALGORITHMFACTORY_H +// #define RAPTORALGORITHMFACTORY_H +// +// #include "IRaptorAlgorithmFactory.h" +// #include "strategies/RaptorStrategy.h" +// +// #include +// #include +// #include +// +// namespace raptor::strategy::factory { +// +// class RAPTOR_API RaptorAlgorithmFactory final : public IRaptorAlgorithmFactory +// { +// public: +// std::unique_ptr create(AlgorithmType type, schedule::gtfs::TimetableManager&& relationManager) override; +// +// private: +// std::map(schedule::gtfs::TimetableManager&& relationManager)>> strategies{ +// {RAPTOR, [](schedule::gtfs::TimetableManager&& relationManager) { return std::make_unique(std::move(relationManager)); }}}; +// }; +// +// } // factory +// // strategy +// // gtfs +// // raptor +// +// #endif //RAPTORALGORITHMFACTORY_H diff --git a/raptor/src/RaptorConnection.cpp b/raptor/src/RaptorConnection.cpp new file mode 100644 index 00000000..7ab94cae --- /dev/null +++ b/raptor/src/RaptorConnection.cpp @@ -0,0 +1,94 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#include "RaptorConnection.h" +#include +#include + +namespace raptor { + + void RaptorConnection::validateLegOrder(const std::shared_ptr& current, const std::shared_ptr& next) { + if (current->getToStopId() != next->getFromStopId()) + { + throw std::invalid_argument(std::format("Legs are not connected: {} -> {}", current->getToStopId(), next->getFromStopId())); + } + if (current->getArrivalTime() < current->getDepartureTime()) + { + throw std::invalid_argument("Arrival time must be after departure time."); + } + if (current->getArrivalTime() > next->getDepartureTime()) + { + throw std::invalid_argument("Arrival time must be before next departure time."); + } + } + + void RaptorConnection::addLeg(const std::shared_ptr& leg) { + legs.push_back(leg); + } + + void RaptorConnection::initialize() { + + std::ranges::sort(legs, [](const auto& aLhs, const auto& aRhs) { + return *aLhs < *aRhs; + }); + for (auto i = 0; i < legs.size() - 1; ++i) + { + validateLegOrder(legs[i], legs[i + 1]); + } + legs.shrink_to_fit(); + } + + int RaptorConnection::getDepartureTime() const { + return legs.front()->getDepartureTime(); + } + + int RaptorConnection::getArrivalTime() const { + return legs.back()->getArrivalTime(); + } + + std::string RaptorConnection::getFromStopId() const { + return legs.front()->getFromStopId(); + } + + std::string RaptorConnection::getToStopId() const { + return legs.back()->getToStopId(); + } + + int RaptorConnection::getDurationInSeconds() const { + return getArrivalTime() - getDepartureTime(); + } + + std::vector> RaptorConnection::getWalkTransfers() const { + std::vector> walkTransfers; + std::ranges::copy_if(legs, std::back_inserter(walkTransfers), [](const auto& leg) { return leg->getType() == Leg::Type::WALK_TRANSFER; }); + return walkTransfers; + } + + std::vector> RaptorConnection::getRouteLegs() const { + std::vector> routeLegs; + std::ranges::copy_if(legs, std::back_inserter(routeLegs), [](const auto& leg) { return leg->getType() == Leg::Type::ROUTE; }); + return routeLegs; + } + + types::raptorInt RaptorConnection::getNumberOfSameStopTransfers() const { + types::raptorInt transferCounter = 0; + for (size_t i = 0; i < legs.size() - 1; ++i) + { + if (legs[i]->getType() == Leg::Type::ROUTE + && legs[i + 1]->getType() == Leg::Type::ROUTE) + { + ++transferCounter; + } + } + return transferCounter; + } + + types::raptorInt RaptorConnection::getNumberOfTotalTransfers() const { + return static_cast(getWalkTransfers().size() + getNumberOfSameStopTransfers()); + } + + std::vector> RaptorConnection::getLegs() const { + return legs; + } +} // raptor \ No newline at end of file diff --git a/raptor/src/RaptorConnection.h b/raptor/src/RaptorConnection.h new file mode 100644 index 00000000..027d1e55 --- /dev/null +++ b/raptor/src/RaptorConnection.h @@ -0,0 +1,48 @@ +// +// Created by MichaelBrunner on 02/08/2024. +// + +#pragma once +#include "Connection.h" +#include "Leg.h" +#include "usingTypes.h" + + +#include + +namespace raptor { + + class RaptorConnection final : public Connection + { + std::vector> legs; + + static void validateLegOrder(const std::shared_ptr& current, const std::shared_ptr& next); + + public: + RaptorConnection() = default; + + void addLeg(const std::shared_ptr& leg); + void initialize(); + + [[nodiscard]] int getDepartureTime() const override; + + [[nodiscard]] int getArrivalTime() const override; + + [[nodiscard]] std::string getFromStopId() const override; + + [[nodiscard]] std::string getToStopId() const override; + + [[nodiscard]] int getDurationInSeconds() const override; + + [[nodiscard]] std::vector> getWalkTransfers() const override; + + [[nodiscard]] std::vector> getRouteLegs() const override; + + [[nodiscard]] types::raptorInt getNumberOfSameStopTransfers() const override; + + [[nodiscard]] types::raptorInt getNumberOfTotalTransfers() const override; + + [[nodiscard]] std::vector> getLegs() const override; + }; + +} // raptor diff --git a/raptor/src/RaptorRouter.cpp b/raptor/src/RaptorRouter.cpp new file mode 100644 index 00000000..2aa4501b --- /dev/null +++ b/raptor/src/RaptorRouter.cpp @@ -0,0 +1,166 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#include "RaptorRouter.h" + +#include "LabelPostprocessor.h" +#include "LoggerFactory.h" +#include "Query.h" +#include "utils/helperFunctions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace raptor { + + namespace validation { + constexpr types::raptorInt MIN_WALKING_TIME_TO_TARGET = 0; + constexpr types::raptorInt MAX_DIFFERENCE_IN_SOURCE_STOP_TIMES = 24 * 60 * 60; + + void checkNonNullOrEmptyStops(const std::map& stops, const std::string& labelSource) { + if (stops.empty()) + { + throw std::invalid_argument(labelSource + " stops must not be empty."); + } + } + + void validateSourceStopTimes(const std::map& sourceStops) { + for (const auto& entry : sourceStops) + { + if (entry.second == 0) + { + throw std::invalid_argument("Source stop times must not be null."); + } + } + + if (const auto minMaxPair = std::minmax_element(sourceStops.begin(), sourceStops.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); minMaxPair.second->second - minMaxPair.first->second > MAX_DIFFERENCE_IN_SOURCE_STOP_TIMES) + { + throw std::invalid_argument("Difference between source stop times must be less than 24 hours."); + } + } + + void validateStopPermutations(const std::map& sourceStops, const std::map& targetStops) { + // Ensure departure and arrival stops are not the same + std::set sourceKeys; + for (const auto& key : sourceStops | std::views::keys) + { + sourceKeys.insert(key); + } + + for (const auto& stop : targetStops | std::views::keys) + { + if (sourceKeys.contains(stop)) + { + throw std::invalid_argument("Source and target stop IDs must not be the same."); + } + } + } + + + std::map validateStopsAndGetIndices(const std::map& stops, const std::unordered_map& stopsToIdx) { + if (stops.empty()) + { + throw std::invalid_argument("At least one stop ID must be provided."); + } + + std::map validStopIds; + for (const auto& [stopId, time] : stops) + { + if (auto it = stopsToIdx.find(stopId); + it != stopsToIdx.end()) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::to_string(it->second)); + getConsoleLogger(LoggerName::RAPTOR)->info(std::to_string(time)); + validStopIds[it->second] = time; + } + else + { + getConsoleLogger(LoggerName::RAPTOR)->error(std::format("Stop ID {} not found in lookup, removing from query.", stopId)); + } + } + + if (validStopIds.empty()) + { + throw std::invalid_argument("No valid stops provided."); + } + + return validStopIds; + } + + } + + RaptorRouter::RaptorRouter(RaptorData raptorData) + : raptorData(std::move(raptorData)) {} + + std::vector> RaptorRouter::routeEarliestArrival(const std::map& departureStops, + const std::map& arrivalStops, + const config::QueryConfig& config) const { + validation::checkNonNullOrEmptyStops(departureStops, "Departure"); + validation::checkNonNullOrEmptyStops(arrivalStops, "Arrival"); + + auto departureStopIds = departureStops | std::views::keys; + auto arrivalStopIds = arrivalStops | std::views::keys; + auto departureAt = departureStops | std::views::values; + + std::string departureStopIdsStr = utils::joinToString(departureStopIds); + std::string arrivalStopIdsStr = utils::joinToString(arrivalStopIds); + std::string departureAtStr = utils::joinToString(departureAt); + + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Routing earliest arrival from {} to {} departing at {}", departureStopIdsStr, arrivalStopIdsStr, departureAtStr)); + + auto validatedSourceStops = validation::validateStopsAndGetIndices(departureStops, raptorData.getLookup().stops); + auto validatedTargetStops = validation::validateStopsAndGetIndices(arrivalStops, raptorData.getLookup().stops); + + const auto sourceStopIndices = validatedSourceStops | std::views::keys | std::ranges::to>(); + const auto targetStopIndices = validatedTargetStops | std::views::keys | std::ranges::to>(); + const auto sourceTimes = validatedSourceStops | std::views::values | std::ranges::to>(); + const auto walkingDurationsToTarget = validatedTargetStops | std::views::values | std::ranges::to>(); + + const auto queryParams = QueryParams{ + .raptorData = raptorData, + .sourceStopIndices = sourceStopIndices, + .targetStopIndices = targetStopIndices, + .sourceTimes = sourceTimes, + .walkingDurationsToTarget = walkingDurationsToTarget, + .config = config}; + + auto query = Query(queryParams); + const auto& bestLabelsPerRound = query.run(); + + auto referenceDate = std::ranges::min(validatedSourceStops | std::views::values); + + auto connection = LabelPostprocessor(*this); + return connection.reconstructParetoOptimalSolutions(bestLabelsPerRound, validatedTargetStops, referenceDate); + } + + std::vector> RaptorRouter::routeLatestDeparture(const std::map& departureStops, const std::map& arrivalStops, const config::QueryConfig& config) const { + throw std::runtime_error("Not implemented"); + } + + std::map> RaptorRouter::routeIsolines(const std::map& sourceStops, const config::QueryConfig& config) const { + throw std::runtime_error("Not implemented"); + } + + const RaptorData& RaptorRouter::getRaptorData() const { + return raptorData; + } + + + std::vector> RaptorRouter::getConnections(const std::map& sourceStops, const std::map& targetStops, const config::QueryConfig& config) const { + validation::validateSourceStopTimes(sourceStops); + + // Mocked implementation for processing + std::vector> connections; + // Implement actual logic using DateTimeUtils, Query, LabelPostprocessor, etc. + return connections; + } +} \ No newline at end of file diff --git a/raptor/src/RaptorRouter.h b/raptor/src/RaptorRouter.h new file mode 100644 index 00000000..7b602cb5 --- /dev/null +++ b/raptor/src/RaptorRouter.h @@ -0,0 +1,45 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#ifndef RAPTOR_ROUTER_H +#define RAPTOR_ROUTER_H + +#include "QueryConfig.h" +#include "RaptorAlgorithm.h" +#include "utils/RaptorData.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace raptor { + + class RAPTOR_API RaptorRouter final : public RaptorAlgorithm + { + public: + explicit RaptorRouter(RaptorData raptorData); + + [[nodiscard]] std::vector> routeEarliestArrival(const std::map& departureStops, const std::map& arrivalStops, const config::QueryConfig& config) const override; + + [[nodiscard]] std::vector> routeLatestDeparture(const std::map& departureStops, const std::map& arrivalStops, const config::QueryConfig& config) const override; + + [[nodiscard]] std::map> routeIsolines(const std::map& sourceStops, const config::QueryConfig& config) const override; + + [[nodiscard]] const RaptorData& getRaptorData() const; + + + private: + [[nodiscard]] std::vector> getConnections(const std::map& sourceStops, + const std::map& targetStops, + const config::QueryConfig& config) const; + + RaptorData raptorData; + }; +} + +#endif // RAPTOR_ROUTER_H diff --git a/raptor/src/RouteScanner.cpp b/raptor/src/RouteScanner.cpp new file mode 100644 index 00000000..92c612e6 --- /dev/null +++ b/raptor/src/RouteScanner.cpp @@ -0,0 +1,189 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#include "RouteScanner.h" + +#include "LoggerFactory.h" +#include "data/ActiveTrip.h" + +#include + +namespace raptor { + + bool canEnterAtStop(const Stop& stop, const types::raptorInt stopTime, const std::unordered_set& markedStops, const types::raptorIdx stopIdx, const types::raptorInt stopOffset, const types::raptorInt numberOfStops) { + + if (constexpr auto unreachableValue = types::INFINITY_VALUE_MAX; + stopTime == unreachableValue) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} cannot be reached", stop.id)); + return false; + } + + if (!markedStops.contains(stopIdx)) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} was not improved in previous round, continue", stop.id)); + return false; + } + + if (stopOffset + 1 == numberOfStops) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} is last stop in route, continue", stop.id)); + return false; + } + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} is first stop in route, continue", stop.id)); + + return true; + } + + RouteScanner::RouteScanner(StopLabelsAndTimes& stopLabelsAndTimes, const RaptorData& raptorData, const types::raptorInt minimumTransferDuration) + : stops(raptorData.getStopContext().stops) + , stopRoutes(raptorData.getStopContext().stopRoutes) + , stopTimes(raptorData.getRouteTraversal().stopTimes) + , routes(raptorData.getRouteTraversal().routes) + , routeStops(raptorData.getRouteTraversal().routeStops) + , stopLabelsAndTimes(const_cast(stopLabelsAndTimes)) + , minTransferDuration(minimumTransferDuration) { + } + + std::unordered_set RouteScanner::scan(const types::raptorInt round, const std::unordered_set& markedStops) const { + const std::unordered_set routesToScan = getRoutesToScan(markedStops); + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Scanning routes for round {} ({} routes)", round, routesToScan.size())); + + std::unordered_set markedStopsNext; + + for (const auto currentRouteIdx : routesToScan) + { + scanRoute(currentRouteIdx, round, markedStops, markedStopsNext); + } + + return markedStopsNext; + } + + std::unordered_set RouteScanner::getRoutesToScan(const std::unordered_set& markedStops) const { + std::unordered_set routesToScan; + + for (const auto stopIdx : markedStops) + { + const Stop& currentStop = stops[stopIdx]; + auto stopRouteIdx = currentStop.stopRouteIndex; + const auto stopRouteEndIdx = stopRouteIdx + currentStop.numberOfRoutes; + + while (stopRouteIdx < stopRouteEndIdx) + { + routesToScan.insert(stopRoutes[stopRouteIdx]); + ++stopRouteIdx; + } + } + + return routesToScan; + } + + void RouteScanner::scanRoute(const types::raptorIdx currentRouteIdx, const types::raptorInt round, const std::unordered_set& markedStops, std::unordered_set& markedStopsNext) const { + const auto lastRound = round - 1; + + const Route& currentRoute = routes[currentRouteIdx]; + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Scanning route {}", currentRoute.id)); + + const auto firstRouteStopIdx = currentRoute.firstRouteStopIndex; + const auto firstStopTimeIdx = currentRoute.firstStopTimeIndex; + const auto numberOfStops = currentRoute.numberOfStops; + + std::shared_ptr activeTrip = nullptr; + + for (auto stopOffset = 0u; stopOffset < numberOfStops; stopOffset++) + { + const auto stopIdx = routeStops[firstRouteStopIdx + stopOffset].stopIndex; + const Stop& stop = stops[stopIdx]; + const auto bestStopTime = stopLabelsAndTimes.getComparableBestTime(stopIdx); + + if (activeTrip == nullptr) + { + if (!canEnterAtStop(stop, bestStopTime, markedStops, stopIdx, stopOffset, numberOfStops)) + { + continue; + } + } + else + { + const StopTime& stopTimeObj = stopTimes[firstStopTimeIdx + activeTrip->tripOffset * numberOfStops + stopOffset]; + if (!checkIfTripIsPossibleAndUpdateMarks(stopTimeObj, activeTrip, stop, bestStopTime, stopIdx, round, lastRound, markedStopsNext, currentRouteIdx)) + { + continue; + } + } + + activeTrip = findPossibleTrip(stopIdx, stop, stopOffset, currentRoute, lastRound); + } + } + + bool RouteScanner::checkIfTripIsPossibleAndUpdateMarks(const StopTime& stopTime, const std::shared_ptr& activeTrip, const Stop& stop, const types::raptorInt bestStopTime, types::raptorIdx stopIdx, const types::raptorInt thisRound, const types::raptorInt lastRound, std::unordered_set& markedStopsNext, types::raptorIdx currentRouteIdx) const { + + if (const bool isImproved = stopTime.arrival < bestStopTime) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} was improved", stop.id)); + stopLabelsAndTimes.setBestTime(stopIdx, stopTime.arrival); + + auto label = std::make_unique(activeTrip->entryTime, + stopTime.arrival, + StopLabelsAndTimes::LabelType::ROUTE, + currentRouteIdx, + activeTrip->tripOffset, + stopIdx, + activeTrip->previousLabel); + + stopLabelsAndTimes.setLabel(thisRound, stopIdx, std::move(label)); + markedStopsNext.insert(stopIdx); + + return false; + } + + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} was not improved", stop.id)); + const auto previous = stopLabelsAndTimes.getLabel(lastRound, stopIdx); + + if (const bool isImprovedInSameRound = previous == nullptr || previous->targetTime >= stopTime.arrival) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} has been improved in same round, trip not possible within this round", stop.id)); + return false; + } + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Stop {} has not been improved in same round, checking for trips", stop.id)); + return true; + } + + std::shared_ptr RouteScanner::findPossibleTrip(const types::raptorIdx stopIdx, const Stop& stop, const types::raptorInt stopOffset, const Route& route, const types::raptorInt lastRound) const { + const auto firstStopTimeIdx = route.firstStopTimeIndex; + const auto numberOfStops = route.numberOfStops; + const auto numberOfTrips = route.numberOfTrips; + + types::raptorInt tripOffset = 0; + auto previousLabel = stopLabelsAndTimes.getLabel(lastRound, stopIdx); + + auto referenceTime = previousLabel->targetTime; + if (previousLabel->type == StopLabelsAndTimes::LabelType::ROUTE) + { + referenceTime += std::max(stop.sameStopTransferTime, minTransferDuration); + } + + while (tripOffset < numberOfTrips) + { + const StopTime& currentStopTime = stopTimes[firstStopTimeIdx + tripOffset * numberOfStops + stopOffset]; + if (currentStopTime.departure >= referenceTime) + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("Found active trip ({}) on route {}", tripOffset, route.id)); + types::raptorInt entryTime = currentStopTime.departure; + return std::make_shared(tripOffset, entryTime, previousLabel); + } + if (tripOffset < numberOfTrips - 1) + { + tripOffset += 1; + } + else + { + getConsoleLogger(LoggerName::RAPTOR)->info(std::format("No active trip found on route {}", route.id)); + return nullptr; + } + } + + return nullptr; + } +} // raptor \ No newline at end of file diff --git a/raptor/src/RouteScanner.h b/raptor/src/RouteScanner.h new file mode 100644 index 00000000..ceaee8ca --- /dev/null +++ b/raptor/src/RouteScanner.h @@ -0,0 +1,48 @@ +// +// Created by MichaelBrunner on 20/07/2024. +// + +#ifndef ROUTESCANNER_H +#define ROUTESCANNER_H + +#include "data/ActiveTrip.h" +#include "utils/StopLabelsAndTimes.h" +#include "utils/RaptorData.h" + +#include +#include +#include +#include +#include +#include + +namespace raptor { + + class RouteScanner + { + public: + RouteScanner(StopLabelsAndTimes& stopLabelsAndTimes, const RaptorData& raptorData, types::raptorInt minimumTransferDuration); + + std::unordered_set scan(types::raptorInt round, const std::unordered_set& markedStops) const; + + private: + [[nodiscard]] std::unordered_set getRoutesToScan(const std::unordered_set& markedStops) const; + + void scanRoute(types::raptorIdx currentRouteIdx, types::raptorInt round, const std::unordered_set& markedStops, std::unordered_set& markedStopsNext) const; + + bool checkIfTripIsPossibleAndUpdateMarks(const StopTime& stopTime, const std::shared_ptr& activeTrip, const Stop& stop, types::raptorInt bestStopTime, types::raptorIdx stopIdx, types::raptorInt thisRound, types::raptorInt lastRound, std::unordered_set& markedStopsNext, types::raptorIdx currentRouteIdx) const; + + [[nodiscard]] std::shared_ptr findPossibleTrip(types::raptorIdx stopIdx, const Stop& stop, types::raptorInt stopOffset, const Route& route, types::raptorInt lastRound) const; + + const std::vector& stops; + const std::vector& stopRoutes; + const std::vector& stopTimes; + const std::vector& routes; + const std::vector& routeStops; + StopLabelsAndTimes& stopLabelsAndTimes; + const types::raptorInt minTransferDuration; + }; + +} // raptor + +#endif //ROUTESCANNER_H diff --git a/raptor/src/data/ActiveTrip.h b/raptor/src/data/ActiveTrip.h new file mode 100644 index 00000000..9eb49570 --- /dev/null +++ b/raptor/src/data/ActiveTrip.h @@ -0,0 +1,23 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +#include "../utils/StopLabelsAndTimes.h" + +namespace raptor { + + struct ActiveTrip + { + int tripOffset; + int entryTime; + const StopLabelsAndTimes::Label* previousLabel; + + ActiveTrip(const int tripOffset, const int entryTime, const StopLabelsAndTimes::Label* previousLabel) + : tripOffset(tripOffset) + , entryTime(entryTime) + , previousLabel(previousLabel) {} + }; + +} \ No newline at end of file diff --git a/raptor/src/data/raptorRouteStructures.h b/raptor/src/data/raptorRouteStructures.h new file mode 100644 index 00000000..0eedea11 --- /dev/null +++ b/raptor/src/data/raptorRouteStructures.h @@ -0,0 +1,93 @@ +// +// Created by MichaelBrunner on 26/07/2024. +// + +#pragma once + +#include "usingTypes.h" + + +#include +#include +#include + +namespace raptor { + + struct RouteStop + { + types::raptorIdx stopIndex; + types::raptorIdx routeIndex; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct StopTime + { + types::raptorInt arrival; + types::raptorInt departure; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct Route + { + std::string id; + types::raptorIdx firstRouteStopIndex; + types::raptorInt numberOfStops; + types::raptorIdx firstStopTimeIndex; + types::raptorInt numberOfTrips; + std::vector tripIds; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct Stop + { + std::string id; + types::raptorIdx stopRouteIndex; + types::raptorInt numberOfRoutes; + types::raptorInt sameStopTransferTime; + int transferIndex; + types::raptorInt numberOfTransfers; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct Transfer + { + types::raptorIdx targetStopIndex; + types::raptorInt duration; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct StopRoutesIndexLookup + { + std::unordered_map stops; + std::unordered_map routes; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct StopContext + { + std::vector transfers; + std::vector stops; + std::vector stopRoutes; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct RouteTraversal + { + std::vector routeStops; + std::vector stopTimes; + std::vector routes; + + RouteTraversal(std::vector routeStops, std::vector stopTimes, std::vector routes) + : routeStops(std::move(routeStops)) + , stopTimes(std::move(stopTimes)) + , routes(std::move(routes)) { + } + }; +} \ No newline at end of file diff --git a/raptor/src/main.cpp b/raptor/src/main.cpp deleted file mode 100644 index 0ce6e60c..00000000 --- a/raptor/src/main.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// -// Created by MichaelBrunner on 28/05/2024. -// - -/////// PSEUDO IMPLEMENTATION OF RAPTOR ALGORITHM /////// - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct Footpath; -struct Route; - -// Define the Stop structure -struct Stop -{ - int id; // Unique identifier for the stop - std::vector routes; // List of routes that serve this stop - - std::vector footpaths; // List of footpaths from this stop - - // Function to get the routes that serve this stop - [[nodiscard]] std::vector served_routes() const { - return routes; - } - - [[nodiscard]] std::vector footpaths_from() const { - return footpaths; - } - - static int count; // Total number of stops -}; - -// Define the Route structure -struct Route -{ - int id; // Unique identifier for the route - std::vector stops; // List of stops that this route serves - - // Function to get the stops served by this route from a given stop - std::vector stops_from(Stop* stop) { - auto it = std::ranges::find(stops, stop); - if (it != stops.end()) - { - return std::vector(it, stops.end()); - } - else - { - return std::vector(); - } - } -}; - -// Define the Trip structure -struct Trip -{ - int id; // Unique identifier for the trip - Route* route; // The route that this trip follows - std::vector arrival_times; // Arrival times at each stop - std::vector departure_times; // Departure times from each stop - - // Assuming arr and dep are defined elsewhere -}; - -// Define the Footpath structure -struct Footpath -{ - Stop* from; // The stop from which the footpath starts - Stop* to; // The stop at which the footpath ends - int distance; // The distance of the footpath -}; - - -constexpr auto INFINITY_VALUE = std::numeric_limits::max(); - -// Define the arr function -int arr(const Trip& trip, const Stop& stop) { - // Assuming the stops in a trip and the arrival times have the same order - auto it = std::find(trip.route->stops.begin(), trip.route->stops.end(), &stop); - if (it != trip.route->stops.end()) { - return trip.arrival_times[it - trip.route->stops.begin()]; - } else { - return INFINITY_VALUE; - } -} - -// Define the dep function -int dep(const Trip& trip, const Stop& stop) { - // Assuming the stops in a trip and the departure times have the same order - auto it = std::find(trip.route->stops.begin(), trip.route->stops.end(), &stop); - if (it != trip.route->stops.end()) { - return trip.departure_times[it - trip.route->stops.begin()]; - } else { - return INFINITY_VALUE; - } -} - -// Define the et function -Trip et(const Route& route, const Stop& stop) { - // This function should return the earliest trip that can be taken from the stop on the route - // The implementation of this function depends on how the trips are stored and accessed - // For the sake of this example, let's assume we have a function get_trips that returns all trips for a route - std::vector trips = get_trips(route); - // Sort the trips by departure time from the stop - std::sort(trips.begin(), trips.end(), [&stop](const Trip& a, const Trip& b) { - return dep(a, stop) < dep(b, stop); - }); - // Return the first trip - return trips.front(); -} - -// Define the contains function for std::queue -template -bool contains(const std::queue>& q, const T1& value) { - // This function checks if a queue of tuples contains a tuple with a specific first value - for (size_t i = 0; i < q.size(); ++i) { - if (std::get<0>(q.front()) == value) { - return true; - } - q.push(q.front()); - q.pop(); - } - return false; -} - -// Define the replace function for std::queue -template -void replace(std::queue>& q, const T1& value, const T2& new_second_value) { - // This function replaces the second value of the first tuple in a queue that has a specific first value - for (size_t i = 0; i < q.size(); ++i) { - if (std::get<0>(q.front()) == value) { - q.front() = std::make_tuple(value, new_second_value); - return; - } - q.push(q.front()); - q.pop(); - } -} - - -// Assuming Stop, Route, Trip, and Footpath are defined elsewhere -// Assuming functions arr(t, p), dep(t, p), et(r, p), and `(p, p0) are defined elsewhere - -struct Label -{ - int time; - Stop parent; - // Additional fields as necessary -}; - -std::vector