From cdb50611e133413b4a5b1bc74cc993deee3e4115 Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 31 Aug 2023 13:30:15 -0400 Subject: [PATCH 1/4] Split osrm_fetcher into 2 classes and a base one The bird distance was renamed to euclidean. This allow to pass the desired object to the Calculator constructor and thus allowing us to select which method we want to use. This replace the global static variable. We don't use the cycling and driving mode so we instanciate an OsrmGeoFilter for those. The OSRM parameters (host/port) are now part of the OsrmGeoFilter object. AccessMode and EgressMode was always walking, we removed those variables from the Paramters object. --- .../include/calculator.hpp | 5 +- .../src/initializations.cpp | 4 +- .../src/parameters/legacyV1_parameters.cpp | 2 - connection_scan_algorithm/src/resets.cpp | 8 +- .../src/transit_routing_http_server.cpp | 13 +- include/euclideangeofilter.hpp | 23 +++ include/geofilter.hpp | 34 ++++ include/osrm_fetcher.hpp | 47 ------ include/osrmgeofilter.hpp | 28 ++++ include/parameters.hpp | 2 - src/Makefile.am | 4 +- src/euclideangeofilter.cpp | 41 +++++ src/geofilter.cpp | 30 ++++ src/osrm_fetcher.cpp | 153 ------------------ src/osrmgeofilter.cpp | 94 +++++++++++ tests/benchmark_csa/Makefile.am | 3 +- tests/benchmark_csa/benchmark_CSA_test.cpp | 15 +- tests/connection_scan_algorithm/Makefile.am | 3 +- .../csa_access_map_test.cpp | 2 - .../csa_route_calculation_test.cpp | 2 - .../csa_route_transfer_alternatives_test.cpp | 2 - .../csa_test_base.hpp | 4 +- .../csa_v1_odtrips_calculation_test.cpp | 2 - .../csa_v1_simple_calculation_test.cpp | 2 - .../csa_v1_transfer_and_alternatives_test.cpp | 2 - 25 files changed, 279 insertions(+), 246 deletions(-) create mode 100644 include/euclideangeofilter.hpp create mode 100644 include/geofilter.hpp delete mode 100644 include/osrm_fetcher.hpp create mode 100644 include/osrmgeofilter.hpp create mode 100644 src/euclideangeofilter.cpp create mode 100644 src/geofilter.cpp delete mode 100644 src/osrm_fetcher.cpp create mode 100644 src/osrmgeofilter.cpp diff --git a/connection_scan_algorithm/include/calculator.hpp b/connection_scan_algorithm/include/calculator.hpp index c4955aec..ba12d554 100644 --- a/connection_scan_algorithm/include/calculator.hpp +++ b/connection_scan_algorithm/include/calculator.hpp @@ -41,6 +41,7 @@ namespace TrRouting class TransitData; class ConnectionSet; class Point; + class GeoFilter; class Calculator { @@ -48,7 +49,7 @@ namespace TrRouting std::map benchmarking; - Calculator(const TransitData &_transitData); + Calculator(const TransitData &_transitData, GeoFilter &_geofilter); void reset(CommonParameters ¶meters, std::optional> origin, std::optional> destination, bool resetAccessPaths = true, bool resetFilters = true); // TODO This function supports both allNodes and simple calculation, which @@ -101,6 +102,8 @@ namespace TrRouting //TODO set it mutable so it can be changed/reset? const TransitData &transitData; + //TODO Should it be const? + GeoFilter &geoFilter; int departureTimeSeconds; int arrivalTimeSeconds; diff --git a/connection_scan_algorithm/src/initializations.cpp b/connection_scan_algorithm/src/initializations.cpp index e811eae3..e64991a3 100644 --- a/connection_scan_algorithm/src/initializations.cpp +++ b/connection_scan_algorithm/src/initializations.cpp @@ -17,14 +17,16 @@ #include "person.hpp" #include "connection_cache.hpp" #include "transit_data.hpp" +#include "geofilter.hpp" namespace TrRouting { - Calculator::Calculator(const TransitData &_transitData) : + Calculator::Calculator(const TransitData &_transitData, GeoFilter &_geoFilter) : algorithmCalculationTime(CalculationTime()), odTripGlob(std::nullopt), transitData(_transitData), + geoFilter(_geoFilter), departureTimeSeconds(0), arrivalTimeSeconds(-1), minAccessTravelTime(0), diff --git a/connection_scan_algorithm/src/parameters/legacyV1_parameters.cpp b/connection_scan_algorithm/src/parameters/legacyV1_parameters.cpp index 62bce661..e9816ec2 100644 --- a/connection_scan_algorithm/src/parameters/legacyV1_parameters.cpp +++ b/connection_scan_algorithm/src/parameters/legacyV1_parameters.cpp @@ -47,8 +47,6 @@ namespace TrRouting maxTotalWalkingTravelTimeSeconds = 60*60; // not used right now maxOnlyWalkingAccessTravelTimeRatio = 1.5; // prefer walking only if it is faster than transit and total only walking travel time <= maxAccessWalkingTravelTimeSeconds * this ratio transferPenaltySeconds = 0; // not used right now - accessMode = "walking"; - egressMode = "walking"; noResultSecondMode = "driving"; tryNextModeIfRoutingFails = false; noResultNextAccessTimeSecondsIncrement = 5*60; diff --git a/connection_scan_algorithm/src/resets.cpp b/connection_scan_algorithm/src/resets.cpp index 1a59d5eb..af826073 100644 --- a/connection_scan_algorithm/src/resets.cpp +++ b/connection_scan_algorithm/src/resets.cpp @@ -2,7 +2,6 @@ #include "calculator.hpp" #include "parameters.hpp" #include "trip.hpp" -#include "osrm_fetcher.hpp" #include "toolbox.hpp" //MAX_INT #include "od_trip.hpp" #include "routing_result.hpp" @@ -13,6 +12,7 @@ #include "node.hpp" #include "transit_data.hpp" #include "connection_set.hpp" +#include "geofilter.hpp" namespace TrRouting { @@ -199,9 +199,9 @@ namespace TrRouting } else { - spdlog::debug(" fetching nodes with osrm with mode {}", params.accessMode); + spdlog::debug(" fetching nodes with osrm"); - accessFootpaths = OsrmFetcher::getAccessibleNodesFootpathsFromPoint(origin, transitData.getNodes(), params.accessMode, parameters.getMaxAccessWalkingTravelTimeSeconds(), params.walkingSpeedMetersPerSecond); + accessFootpaths = geoFilter.getAccessibleNodesFootpathsFromPoint(origin, transitData.getNodes(), parameters.getMaxAccessWalkingTravelTimeSeconds(), params.walkingSpeedMetersPerSecond); if (accessFootpaths.size() == 0) { accessFootpathOk = false; } @@ -240,7 +240,7 @@ namespace TrRouting } else { - egressFootpaths = OsrmFetcher::getAccessibleNodesFootpathsFromPoint(destination, transitData.getNodes(), params.accessMode, parameters.getMaxEgressWalkingTravelTimeSeconds(), params.walkingSpeedMetersPerSecond); + egressFootpaths = geoFilter.getAccessibleNodesFootpathsFromPoint(destination, transitData.getNodes(), parameters.getMaxEgressWalkingTravelTimeSeconds(), params.walkingSpeedMetersPerSecond); if (egressFootpaths.size() == 0) { egressFootpathOk = false; } diff --git a/connection_scan_algorithm/src/transit_routing_http_server.cpp b/connection_scan_algorithm/src/transit_routing_http_server.cpp index c567290e..5521bb13 100644 --- a/connection_scan_algorithm/src/transit_routing_http_server.cpp +++ b/connection_scan_algorithm/src/transit_routing_http_server.cpp @@ -27,8 +27,8 @@ #include "result_to_v2_summary.hpp" #include "result_to_v2_accessibility.hpp" #include "routing_result.hpp" -#include "osrm_fetcher.hpp" #include "transit_data.hpp" +#include "osrmgeofilter.hpp" using namespace TrRouting; @@ -108,13 +108,6 @@ int main(int argc, char** argv) { // setup program options: spdlog::info("Starting transit routing on port {} for the data: {}", programOptions.port, programOptions.cachePath); - OsrmFetcher::osrmWalkingPort = programOptions.osrmWalkingPort; - OsrmFetcher::osrmCyclingPort = programOptions.osrmCyclingPort; - OsrmFetcher::osrmDrivingPort = programOptions.osrmDrivingPort; - OsrmFetcher::osrmWalkingHost = programOptions.osrmWalkingHost; - OsrmFetcher::osrmCyclingHost = programOptions.osrmCyclingHost; - OsrmFetcher::osrmDrivingHost = programOptions.osrmDrivingHost; - if (programOptions.debug) { spdlog::set_level(spdlog::level::debug); } @@ -132,8 +125,8 @@ int main(int argc, char** argv) { //TODO We wanted to handle error in the constructor, but later part of this code expect a dataStatus // leaving as a todo DataStatus dataStatus = transitData.getDataStatus(); - - Calculator calculator(transitData); + OsrmGeoFilter geoFilter("walking", programOptions.osrmWalkingHost, programOptions.osrmWalkingPort); + Calculator calculator(transitData, geoFilter); //TODO, should this be in the constructor? calculator.initializeCalculationData(); spdlog::info("preparing server..."); diff --git a/include/euclideangeofilter.hpp b/include/euclideangeofilter.hpp new file mode 100644 index 00000000..87b464cf --- /dev/null +++ b/include/euclideangeofilter.hpp @@ -0,0 +1,23 @@ +#ifndef TR_EUCLIDEAN_GEO_FILTER +#define TR_EUCLIDEAN_GEO_FILTER + +#include "geofilter.hpp" +#include + +namespace TrRouting +{ + /* Filter nodes using Euclidean distance */ + class EuclideanGeoFilter : public GeoFilter + { + public: + EuclideanGeoFilter(); + + virtual std::vector getAccessibleNodesFootpathsFromPoint(const Point &point, + const std::map &nodes, + int maxWalkingTravelTime, + float walkingSpeedMetersPerSecond, + bool reversed = false); + }; +} + +#endif // TR_EUCLIDEAN_GEO_FILTER diff --git a/include/geofilter.hpp b/include/geofilter.hpp new file mode 100644 index 00000000..c2cf05c4 --- /dev/null +++ b/include/geofilter.hpp @@ -0,0 +1,34 @@ +#ifndef TR_GEO_FILTER +#define TR_GEO_FILTER + +#include +#include //For node map types, TODO have a typedef somewhere for the nodes array +#include +#include + +namespace TrRouting +{ + class Point; + class Node; + class NodeTimeDistance; + + /* Base class to implement filter based in geography */ + class GeoFilter + { + public: + virtual std::vector getAccessibleNodesFootpathsFromPoint(const Point &point, + const std::map &nodes, + int maxWalkingTravelTime, + float walkingSpeedMetersPerSecond, + bool reversed = false) = 0; + protected: + // Common utility functions + static std::tuple calculateLengthOfOneDegree(const Point &point); + static float calculateMaxDistanceSquared(int maxWalkingTravelTime, float walkingSpeed); + //TODO WHy one point * and other & + static float calculateNodeDistanceSquared(const Point *node, const Point &point, const std::tuple &lengthOfOneDegree); + + }; +} + +#endif // TR_GEO_FILTER diff --git a/include/osrm_fetcher.hpp b/include/osrm_fetcher.hpp deleted file mode 100644 index 671bf90f..00000000 --- a/include/osrm_fetcher.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef TR_OSRM_FETCHER -#define TR_OSRM_FETCHER - - -#include -#include -#include -#include - -#include //For node map types, TODO have a typedef somewhere for the nodes array -#include - - -namespace TrRouting -{ - class Point; - class Node; - class NodeTimeDistance; - - class OsrmFetcher - { - - public: - //TODO Eventually, this data should be private to an instance, but for now all use case are static - static std::string osrmWalkingPort; - static std::string osrmCyclingPort; - static std::string osrmDrivingPort; - static std::string osrmWalkingHost; - static std::string osrmCyclingHost; - static std::string osrmDrivingHost; - - //TODO This should be managed outside of the osrm_fetcher, with a dedicated data fetcher - static bool birdDistanceAccessibilityEnabled; // true if the accessibility information is obtained using bird distances instead of osrm - - static std::vector getAccessibleNodesFootpathsFromPoint(const Point &point, const std::map &nodes, std::string mode, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond, bool reversed = false); - - protected: - static std::vector getNodesFromBirdDistance(const Point &point, const std::map &nodes, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond); - static std::vector getNodesFromOsrm(const Point &point, const std::map &nodes, std::string mode, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond, bool reversed); - - static std::tuple calculateLengthOfOneDegree(const Point &point); - static float calculateMaxDistanceSquared(int maxWalkingTravelTime, float walkingSpeed); - static float calculateNodeDistanceSquared(const Point *node, const Point &point, const std::tuple &lengthOfOneDegree); - }; -} - -#endif // TR_OSRM_FETCHER diff --git a/include/osrmgeofilter.hpp b/include/osrmgeofilter.hpp new file mode 100644 index 00000000..e331d5e1 --- /dev/null +++ b/include/osrmgeofilter.hpp @@ -0,0 +1,28 @@ +#ifndef TR_OSRM_GEO_FILTER +#define TR_OSRM_GEO_FILTER + +#include "geofilter.hpp" +#include + +namespace TrRouting +{ + /* Filter nodes using OSRM */ + class OsrmGeoFilter : public GeoFilter + { + public: + OsrmGeoFilter(const std::string &mode, const std::string &host, const std::string & port); + + virtual std::vector getAccessibleNodesFootpathsFromPoint(const Point &point, + const std::map &nodes, + int maxWalkingTravelTime, + float walkingSpeedMetersPerSecond, + bool reversed = false); + protected: + std::string mode; + std::string host; + std::string port; //Could be an int, but it's used as a string every where. Keep a string remove conversions + + }; +} + +#endif // TR_OSRM_GEO_FILTER diff --git a/include/parameters.hpp b/include/parameters.hpp index ec1142f4..0d696bc9 100644 --- a/include/parameters.hpp +++ b/include/parameters.hpp @@ -281,8 +281,6 @@ namespace TrRouting std::optional startingNodeUuid; std::optional endingNodeUuid; - std::string accessMode; - std::string egressMode; bool tryNextModeIfRoutingFails; std::string noResultSecondMode; int noResultNextAccessTimeSecondsIncrement; diff --git a/src/Makefile.am b/src/Makefile.am index 6d9c4bd5..7826ebd1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,9 @@ lines_cache_fetcher.cpp \ modes_initialization.cpp \ nodes_cache_fetcher.cpp \ od_trips_cache_fetcher.cpp \ -osrm_fetcher.cpp \ +geofilter.cpp \ +euclideangeofilter.cpp \ +osrmgeofilter.cpp \ paths_cache_fetcher.cpp \ persons_cache_fetcher.cpp \ scenarios_cache_fetcher.cpp \ diff --git a/src/euclideangeofilter.cpp b/src/euclideangeofilter.cpp new file mode 100644 index 00000000..8abdfedf --- /dev/null +++ b/src/euclideangeofilter.cpp @@ -0,0 +1,41 @@ +#include "euclideangeofilter.hpp" +#include "point.hpp" +#include "node.hpp" +#include "spdlog/spdlog.h" + +namespace TrRouting +{ + + EuclideanGeoFilter::EuclideanGeoFilter() {} + + + std::vector EuclideanGeoFilter::getAccessibleNodesFootpathsFromPoint(const Point &point, + const std::map &nodes, + int maxWalkingTravelTime, + float walkingSpeedMetersPerSecond, + bool /*Unused reversed*/) { + std::vector accessibleNodesFootpaths; + + auto lengthOfOneDegree = calculateLengthOfOneDegree(point); + float maxDistanceMetersSquared = calculateMaxDistanceSquared(maxWalkingTravelTime, walkingSpeedMetersPerSecond); + float distanceMetersSquared; + + spdlog::debug("use of bird distance "); + + for (auto &&[uuid,node] : nodes) + { + distanceMetersSquared = calculateNodeDistanceSquared(node.point.get(), point, lengthOfOneDegree); + + if (distanceMetersSquared <= maxDistanceMetersSquared) + { + int distanceMeters = sqrt(distanceMetersSquared); + int travelTimeSeconds = distanceMeters / walkingSpeedMetersPerSecond; + accessibleNodesFootpaths.push_back(NodeTimeDistance(node, travelTimeSeconds, distanceMeters)); + } + } + + spdlog::debug("fetched footpaths using bird distance ({} footpaths found)", accessibleNodesFootpaths.size()); + + return accessibleNodesFootpaths; + } +} diff --git a/src/geofilter.cpp b/src/geofilter.cpp new file mode 100644 index 00000000..ef9821f3 --- /dev/null +++ b/src/geofilter.cpp @@ -0,0 +1,30 @@ +#include "geofilter.hpp" +#include "point.hpp" +#include "node.hpp" + +namespace TrRouting +{ + std::tuple GeoFilter::calculateLengthOfOneDegree(const Point &point) + { + // Taylor series approximation of the longitude and latitude length functions (For more info: https://gis.stackexchange.com/questions/75528/understanding-terms-in-length-of-degree-formula) + float lengthOfOneDegreeOfLongitude = 111412.84 * cos(point.latitude * M_PI / 180) - 93.5 * cos(3 * point.latitude * M_PI / 180); + float lengthOfOneDegreeOflatitude = 111132.92 - 559.82 * cos(2 * point.latitude * M_PI / 180) + 1.175 * cos(4 * point.latitude * M_PI / 180); + + return std::make_tuple(lengthOfOneDegreeOfLongitude, lengthOfOneDegreeOflatitude); + } + + float GeoFilter::calculateMaxDistanceSquared(int maxWalkingTravelTime, float walkingSpeedMetersPerSecond) + { + return (maxWalkingTravelTime * walkingSpeedMetersPerSecond) * (maxWalkingTravelTime * walkingSpeedMetersPerSecond); + } + + float GeoFilter::calculateNodeDistanceSquared(const Point *node, const Point &point, const std::tuple &lengthOfOneDegree) + { + float distanceXMeters = (node->longitude - point.longitude) * std::get<0>(lengthOfOneDegree); + float distanceYMeters = (node->latitude - point.latitude) * std::get<1>(lengthOfOneDegree); + float distanceMetersSquared = distanceXMeters * distanceXMeters + distanceYMeters * distanceYMeters; + + return distanceMetersSquared; + } + +} diff --git a/src/osrm_fetcher.cpp b/src/osrm_fetcher.cpp deleted file mode 100644 index f481747e..00000000 --- a/src/osrm_fetcher.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "osrm_fetcher.hpp" -#include "json.hpp" -#include "point.hpp" -#include "node.hpp" -#include "client_http.hpp" -#include "spdlog/spdlog.h" - -namespace TrRouting -{ - - //TODO Remove this initialization when this data is not static anymore - bool OsrmFetcher::birdDistanceAccessibilityEnabled = false; - std::string OsrmFetcher::osrmWalkingPort=""; - std::string OsrmFetcher::osrmCyclingPort=""; - std::string OsrmFetcher::osrmDrivingPort=""; - std::string OsrmFetcher::osrmWalkingHost=""; - std::string OsrmFetcher::osrmCyclingHost=""; - std::string OsrmFetcher::osrmDrivingHost=""; - - std::vector OsrmFetcher::getAccessibleNodesFootpathsFromPoint(const Point &point, const std::map &nodes, std::string mode, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond, bool reversed) - { - if (OsrmFetcher::birdDistanceAccessibilityEnabled) - { - return getNodesFromBirdDistance(point, nodes, maxWalkingTravelTime, walkingSpeedMetersPerSecond); - } - else - { - return getNodesFromOsrm(point, nodes, mode, maxWalkingTravelTime, walkingSpeedMetersPerSecond, reversed); - } - } - - std::vector OsrmFetcher::getNodesFromBirdDistance(const Point &point, const std::map &nodes, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond) - { - std::vector accessibleNodesFootpaths; - - auto lengthOfOneDegree = calculateLengthOfOneDegree(point); - float maxDistanceMetersSquared = calculateMaxDistanceSquared(maxWalkingTravelTime, walkingSpeedMetersPerSecond); - float distanceMetersSquared; - - spdlog::debug("use of bird distance "); - - for (auto &&[uuid,node] : nodes) - { - distanceMetersSquared = calculateNodeDistanceSquared(node.point.get(), point, lengthOfOneDegree); - - if (distanceMetersSquared <= maxDistanceMetersSquared) - { - int distanceMeters = sqrt(distanceMetersSquared); - int travelTimeSeconds = distanceMeters / walkingSpeedMetersPerSecond; - accessibleNodesFootpaths.push_back(NodeTimeDistance(node, travelTimeSeconds, distanceMeters)); - } - } - - spdlog::debug("fetched footpaths using bird distance ({} footpaths found)", accessibleNodesFootpaths.size()); - - return accessibleNodesFootpaths; - } - - std::vector OsrmFetcher::getNodesFromOsrm(const Point &point, const std::map &nodes, std::string mode, int maxWalkingTravelTime, float walkingSpeedMetersPerSecond, bool reversed) - { - std::vector> birdDistanceAccessibleNodeIndexes; - std::vector accessibleNodesFootpaths; - - auto lengthOfOneDegree = calculateLengthOfOneDegree(point); - float maxDistanceMetersSquared = calculateMaxDistanceSquared(maxWalkingTravelTime, walkingSpeedMetersPerSecond); - float distanceMetersSquared; - - spdlog::debug("osrm with host {} and port {}", OsrmFetcher::osrmWalkingHost, OsrmFetcher::osrmWalkingPort); - - std::string queryString = "/table/v1/" + mode + "/" + std::to_string(point.longitude) + "," + std::to_string(point.latitude); - - for (auto &&[uuid,node] : nodes) - { - distanceMetersSquared = calculateNodeDistanceSquared(node.point.get(), point, lengthOfOneDegree); - - if (distanceMetersSquared <= maxDistanceMetersSquared) - { - birdDistanceAccessibleNodeIndexes.push_back(node); - queryString += ";" + std::to_string(node.point.get()->longitude) + "," + std::to_string(node.point.get()->latitude); - } - } - - queryString += "?annotations=duration,distance"; - - if (reversed) - { - queryString += "&destinations=0"; - } - else - { - queryString += "&sources=0"; - } - - using HttpClient = SimpleWeb::Client; - HttpClient client(OsrmFetcher::osrmWalkingHost + ":" + OsrmFetcher::osrmWalkingPort); - auto s = client.request("GET", queryString); - - std::stringstream responseJsonSs; - responseJsonSs << s->content.rdbuf(); - nlohmann::json responseJson = nlohmann::json::parse(responseJsonSs.str()); - - if (responseJson["durations"] != nullptr && responseJson["distances"] != nullptr && responseJson["durations"][0] != nullptr && responseJson["distances"][0] != nullptr) - { - int numberOfDurations = responseJson["durations"][0].size(); - int numberOfDistances = responseJson["distances"][0].size(); - - if (numberOfDurations > 0 && numberOfDistances > 0) - { - int travelTimeSeconds; - int distanceMeters; - for (int i = 1; i < numberOfDurations; i++) // ignore first (duration with itself) - { - travelTimeSeconds = (int)ceil((float)responseJson["durations"][0][i]); - if (travelTimeSeconds <= maxWalkingTravelTime) - { - distanceMeters = (int)ceil((float)responseJson["distances"][0][i]); - accessibleNodesFootpaths.push_back(NodeTimeDistance(birdDistanceAccessibleNodeIndexes[i - 1], - travelTimeSeconds, - distanceMeters)); - } - } - } - } - - spdlog::debug("fetched osrm footpaths ({} footpaths found)", accessibleNodesFootpaths.size()); - - return accessibleNodesFootpaths; - } - - std::tuple OsrmFetcher::calculateLengthOfOneDegree(const Point &point) - { - // Taylor series approximation of the longitude and latitude length functions (For more info: https://gis.stackexchange.com/questions/75528/understanding-terms-in-length-of-degree-formula) - float lengthOfOneDegreeOfLongitude = 111412.84 * cos(point.latitude * M_PI / 180) - 93.5 * cos(3 * point.latitude * M_PI / 180); - float lengthOfOneDegreeOflatitude = 111132.92 - 559.82 * cos(2 * point.latitude * M_PI / 180) + 1.175 * cos(4 * point.latitude * M_PI / 180); - - return std::make_tuple(lengthOfOneDegreeOfLongitude, lengthOfOneDegreeOflatitude); - } - - float OsrmFetcher::calculateMaxDistanceSquared(int maxWalkingTravelTime, float walkingSpeedMetersPerSecond) - { - return (maxWalkingTravelTime * walkingSpeedMetersPerSecond) * (maxWalkingTravelTime * walkingSpeedMetersPerSecond); - } - - float OsrmFetcher::calculateNodeDistanceSquared(const Point *node, const Point &point, const std::tuple &lengthOfOneDegree) - { - float distanceXMeters = (node->longitude - point.longitude) * std::get<0>(lengthOfOneDegree); - float distanceYMeters = (node->latitude - point.latitude) * std::get<1>(lengthOfOneDegree); - float distanceMetersSquared = distanceXMeters * distanceXMeters + distanceYMeters * distanceYMeters; - - return distanceMetersSquared; - } - -} diff --git a/src/osrmgeofilter.cpp b/src/osrmgeofilter.cpp new file mode 100644 index 00000000..8ac08620 --- /dev/null +++ b/src/osrmgeofilter.cpp @@ -0,0 +1,94 @@ +#include "osrmgeofilter.hpp" +#include "json.hpp" +#include "point.hpp" +#include "node.hpp" +#include "client_http.hpp" +#include "spdlog/spdlog.h" + +namespace TrRouting { + + OsrmGeoFilter::OsrmGeoFilter(const std::string &amode, const std::string &ahost, const std::string &aport) : + mode(amode), + host(ahost), + port(aport) + { + } + + std::vector OsrmGeoFilter::getAccessibleNodesFootpathsFromPoint(const Point &point, + const std::map &nodes, + int maxWalkingTravelTime, + float walkingSpeedMetersPerSecond, + bool reversed) + { + std::vector> birdDistanceAccessibleNodeIndexes; + std::vector accessibleNodesFootpaths; + + auto lengthOfOneDegree = calculateLengthOfOneDegree(point); + float maxDistanceMetersSquared = calculateMaxDistanceSquared(maxWalkingTravelTime, walkingSpeedMetersPerSecond); + float distanceMetersSquared; + + spdlog::debug("osrm with host {} and port {}", host, port); + + std::string queryString = "/table/v1/" + mode + "/" + std::to_string(point.longitude) + "," + std::to_string(point.latitude); + + for (auto &&[uuid,node] : nodes) + { + distanceMetersSquared = calculateNodeDistanceSquared(node.point.get(), point, lengthOfOneDegree); + + if (distanceMetersSquared <= maxDistanceMetersSquared) + { + birdDistanceAccessibleNodeIndexes.push_back(node); + queryString += ";" + std::to_string(node.point.get()->longitude) + "," + std::to_string(node.point.get()->latitude); + } + } + + queryString += "?annotations=duration,distance"; + + if (reversed) + { + queryString += "&destinations=0"; + } + else + { + queryString += "&sources=0"; + } + + using HttpClient = SimpleWeb::Client; + HttpClient client(host + ":" + port); + auto s = client.request("GET", queryString); + + //TODO CHeck status of s + + std::stringstream responseJsonSs; + responseJsonSs << s->content.rdbuf(); + nlohmann::json responseJson = nlohmann::json::parse(responseJsonSs.str()); + + if (responseJson["durations"] != nullptr && responseJson["distances"] != nullptr && responseJson["durations"][0] != nullptr && responseJson["distances"][0] != nullptr) + { + int numberOfDurations = responseJson["durations"][0].size(); + int numberOfDistances = responseJson["distances"][0].size(); + + if (numberOfDurations > 0 && numberOfDistances > 0) + { + int travelTimeSeconds; + int distanceMeters; + for (int i = 1; i < numberOfDurations; i++) // ignore first (duration with itself) + { + travelTimeSeconds = (int)ceil((float)responseJson["durations"][0][i]); + if (travelTimeSeconds <= maxWalkingTravelTime) + { + distanceMeters = (int)ceil((float)responseJson["distances"][0][i]); + accessibleNodesFootpaths.push_back(NodeTimeDistance(birdDistanceAccessibleNodeIndexes[i - 1], + travelTimeSeconds, + distanceMeters)); + } + } + } + } + + spdlog::debug("fetched osrm footpaths ({} footpaths found)", accessibleNodesFootpaths.size()); + + return accessibleNodesFootpaths; + } + +} diff --git a/tests/benchmark_csa/Makefile.am b/tests/benchmark_csa/Makefile.am index a5f6ec2b..0787663a 100644 --- a/tests/benchmark_csa/Makefile.am +++ b/tests/benchmark_csa/Makefile.am @@ -18,7 +18,8 @@ gtest_SOURCES = gtest.cpp \ ../../src/connection_set.cpp \ ../../src/connection_cache.cpp \ ../../src/transit_data.cpp \ - ../../src/osrm_fetcher.cpp + ../../src/geofilter.cpp \ + ../../src/euclideangeofilter.cpp #TODO #167 Place/Household removed while refactoring #../../src/households_cache_fetcher.cpp \ diff --git a/tests/benchmark_csa/benchmark_CSA_test.cpp b/tests/benchmark_csa/benchmark_CSA_test.cpp index 8c1e2b99..c973f12b 100644 --- a/tests/benchmark_csa/benchmark_CSA_test.cpp +++ b/tests/benchmark_csa/benchmark_CSA_test.cpp @@ -15,8 +15,8 @@ #include "scenario.hpp" #include "calculator.hpp" #include "benchmark_CSA_test.hpp" -#include "osrm_fetcher.hpp" #include "transit_data.hpp" +#include "euclideangeofilter.hpp" using namespace TrRouting; @@ -24,6 +24,7 @@ const int NB_ITER = 30; // Global test suite variables, they should not be reset for each test TransitData* transitData; //TODO Required only to get the scenario, we might want to get it in another way +EuclideanGeoFilter* geoFilter; Calculator* calculator; std::ofstream benchmarkResultsFile; std::ofstream benchmarkDetailedResultsFile; @@ -64,18 +65,12 @@ class BenchmarkCSATests : public ConstantBenchmarkCSATests // Initialize calculator and parameters. Open the result files and add headers static void SetUpTestSuite() { - OsrmFetcher::osrmWalkingPort = "5000"; - OsrmFetcher::osrmWalkingHost = "localhost"; //"http://localhost"; - OsrmFetcher::osrmCyclingPort = "8000"; - OsrmFetcher::osrmCyclingHost = "localhost"; - OsrmFetcher::osrmDrivingPort = "7000"; - OsrmFetcher::osrmDrivingHost = "localhost"; - CacheFetcher cacheFetcher = TrRouting::CacheFetcher("cache/demo_transition"); transitData = new TrRouting::TransitData(cacheFetcher); - OsrmFetcher::birdDistanceAccessibilityEnabled = true; + // Use simple distance in the benchmark instead of OSRM + geoFilter = new TrRouting::EuclideanGeoFilter(); - calculator = new TrRouting::Calculator(*transitData); + calculator = new TrRouting::Calculator(*transitData, *geoFilter); if (!updateCalculatorFromCache(*transitData)) { ASSERT_EQ(true, false); diff --git a/tests/connection_scan_algorithm/Makefile.am b/tests/connection_scan_algorithm/Makefile.am index 7028be65..4da3b336 100644 --- a/tests/connection_scan_algorithm/Makefile.am +++ b/tests/connection_scan_algorithm/Makefile.am @@ -3,7 +3,8 @@ check_PROGRAMS = csa_test # Consider refactoring or mocking so that files from ../../src are not necessary csa_test_SOURCES = gtest.cpp \ ../../src/calculation_time.cpp \ - ../../src/osrm_fetcher.cpp \ + ../../src/euclideangeofilter.cpp \ + ../../src/geofilter.cpp \ ../../src/connection_set.cpp \ ../../src/connection_cache.cpp \ ../../src/transit_data.cpp \ diff --git a/tests/connection_scan_algorithm/csa_access_map_test.cpp b/tests/connection_scan_algorithm/csa_access_map_test.cpp index a698362b..43417169 100644 --- a/tests/connection_scan_algorithm/csa_access_map_test.cpp +++ b/tests/connection_scan_algorithm/csa_access_map_test.cpp @@ -6,7 +6,6 @@ #include "csa_test_base.hpp" #include "scenario.hpp" #include "toolbox.hpp" //MAX_INT -#include "osrm_fetcher.hpp" // This fixture tests the calculations for the accessibility map from/to a point class AccessMapFixtureTests : public BaseCsaFixtureTests @@ -35,7 +34,6 @@ std::unique_ptr AccessMapFixtureTests::calculateOd(Tr { // FIXME: This needs to be called to set some default values that are still part of the global parameters calculator.params.setDefaultValues(); - TrRouting::OsrmFetcher::birdDistanceAccessibilityEnabled = true; // TODO Shouldn't need to do this, but we do for now, benchmark needs to be started calculator.algorithmCalculationTime.start(); diff --git a/tests/connection_scan_algorithm/csa_route_calculation_test.cpp b/tests/connection_scan_algorithm/csa_route_calculation_test.cpp index d9e57aa3..13e287cd 100644 --- a/tests/connection_scan_algorithm/csa_route_calculation_test.cpp +++ b/tests/connection_scan_algorithm/csa_route_calculation_test.cpp @@ -19,7 +19,6 @@ #include "trip.hpp" #include "od_trip.hpp" #include "node.hpp" -#include "osrm_fetcher.hpp" // TODO: // Test transferable mode, it has separate code path @@ -540,7 +539,6 @@ std::unique_ptr SingleRouteCalculationFixtureTests::ca { // TODO: This needs to be called to set some default values that are still part of the global parameters calculator.params.setDefaultValues(); - TrRouting::OsrmFetcher::birdDistanceAccessibilityEnabled = true; // TODO Shouldn't need to do this, but we do for now, benchmark needs to be started calculator.algorithmCalculationTime.start(); diff --git a/tests/connection_scan_algorithm/csa_route_transfer_alternatives_test.cpp b/tests/connection_scan_algorithm/csa_route_transfer_alternatives_test.cpp index 836e82f7..91628618 100644 --- a/tests/connection_scan_algorithm/csa_route_transfer_alternatives_test.cpp +++ b/tests/connection_scan_algorithm/csa_route_transfer_alternatives_test.cpp @@ -19,7 +19,6 @@ #include "trip.hpp" #include "od_trip.hpp" #include "node.hpp" -#include "osrm_fetcher.hpp" /** * This file covers tests with transfers and requests for alternatives @@ -355,7 +354,6 @@ TrRouting::AlternativesResult SingleTAndACalculationFixtureTests::calculateWithA { // TODO: This needs to be called to set some default values that are still part of the global parameters calculator.params.setDefaultValues(); - TrRouting::OsrmFetcher::birdDistanceAccessibilityEnabled = true; // TODO Shouldn't need to do this, but we do for now, benchmark needs to be started calculator.algorithmCalculationTime.start(); diff --git a/tests/connection_scan_algorithm/csa_test_base.hpp b/tests/connection_scan_algorithm/csa_test_base.hpp index 59ebe4af..05fe1232 100644 --- a/tests/connection_scan_algorithm/csa_test_base.hpp +++ b/tests/connection_scan_algorithm/csa_test_base.hpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" #include "csa_test_data_fetcher.hpp" #include "transit_data.hpp" +#include "euclideangeofilter.hpp" #include "calculator.hpp" #include "routing_result.hpp" @@ -19,11 +20,12 @@ class BaseCsaFixtureTests : public ::testing::Test // A DataFetcher is required to initialize the calculator TestDataFetcher dataFetcher; TrRouting::TransitData transitData; + TrRouting::EuclideanGeoFilter geoFilter; // Calculator is the entry point to run the algorithm, it is part of the test object since (for now) there is nothing specific for its initialization. TrRouting::Calculator calculator; public: - BaseCsaFixtureTests() : dataFetcher(TestDataFetcher()), transitData(TrRouting::TransitData(dataFetcher)), calculator(TrRouting::Calculator(transitData)) {} + BaseCsaFixtureTests() : dataFetcher(TestDataFetcher()), transitData(TrRouting::TransitData(dataFetcher)), calculator(TrRouting::Calculator(transitData, geoFilter)) {} void SetUp(); // Assert the result returned an exception and that the reason matches the expected reason void assertNoRouting(const TrRouting::NoRoutingFoundException& exception, TrRouting::NoRoutingReason expectedReason); diff --git a/tests/connection_scan_algorithm/csa_v1_odtrips_calculation_test.cpp b/tests/connection_scan_algorithm/csa_v1_odtrips_calculation_test.cpp index 48e5f300..aebea42d 100644 --- a/tests/connection_scan_algorithm/csa_v1_odtrips_calculation_test.cpp +++ b/tests/connection_scan_algorithm/csa_v1_odtrips_calculation_test.cpp @@ -19,7 +19,6 @@ #include "trip.hpp" #include "od_trip.hpp" #include "node.hpp" -#include "osrm_fetcher.hpp" /** * This file covers od trips use cases, where the value of od_trips is set to 1 @@ -65,7 +64,6 @@ nlohmann::json RouteOdTripsFixtureTests::calculateOdTrips(std::vector RouteCalculationFixtureTests::calculat transitData.getOdTrips(), transitData.getNodes(), transitData.getDataSources()); - TrRouting::OsrmFetcher::birdDistanceAccessibilityEnabled = true; // TODO Shouldn't need to do this, but we do for now, benchmark needs to be started calculator.algorithmCalculationTime.start(); diff --git a/tests/connection_scan_algorithm/csa_v1_transfer_and_alternatives_test.cpp b/tests/connection_scan_algorithm/csa_v1_transfer_and_alternatives_test.cpp index 64ce42a0..4f16978a 100644 --- a/tests/connection_scan_algorithm/csa_v1_transfer_and_alternatives_test.cpp +++ b/tests/connection_scan_algorithm/csa_v1_transfer_and_alternatives_test.cpp @@ -21,7 +21,6 @@ #include "person.hpp" #include "place.hpp" #include "od_trip.hpp" -#include "osrm_fetcher.hpp" /** * This file covers tests with transfers and requests for alternatives @@ -184,7 +183,6 @@ TrRouting::AlternativesResult TAndACalculationFixtureTests::calculateWithAlterna transitData.getOdTrips(), transitData.getNodes(), transitData.getDataSources()); - TrRouting::OsrmFetcher::birdDistanceAccessibilityEnabled = true; // TODO Shouldn't need to do this, but we do for now, benchmark needs to be started calculator.algorithmCalculationTime.start(); From d166d20b4d85dbf9da47680ec98efd8417679eaa Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 31 Aug 2023 13:59:13 -0400 Subject: [PATCH 2/4] Add a CLI option to use Euclidean distance instead of OSRM Setting the option --useEuclideanDistance=true will selection the EuclideanGeoFilter instead of the OsrmGeoFilter. This is mostly useful for testing and debugging, where you might not have an OSRM deamon running. --- .../include/program_options.hpp | 1 + connection_scan_algorithm/src/program_options.cpp | 7 +++++++ .../src/transit_routing_http_server.cpp | 15 +++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/connection_scan_algorithm/include/program_options.hpp b/connection_scan_algorithm/include/program_options.hpp index 75bb4af7..64ff8344 100644 --- a/connection_scan_algorithm/include/program_options.hpp +++ b/connection_scan_algorithm/include/program_options.hpp @@ -17,6 +17,7 @@ namespace TrRouting int port; bool debug; bool cacheAllConnectionSets; + bool useEuclideanDistance; std::string algorithm; std::string dataFetcherShortname; std::string osrmWalkingPort; diff --git a/connection_scan_algorithm/src/program_options.cpp b/connection_scan_algorithm/src/program_options.cpp index 61c7a1b1..9d18a75b 100644 --- a/connection_scan_algorithm/src/program_options.cpp +++ b/connection_scan_algorithm/src/program_options.cpp @@ -18,6 +18,8 @@ namespace TrRouting { ("cachePath", boost::program_options::value()->default_value("cache"), "cache path"); options.add_options() ("cacheAllConnectionSets", boost::program_options::value() ->default_value(false), "cache all connections set instead of the ones from the last used scenario"); + options.add_options() + ("useEuclideanDistance", boost::program_options::value() ->default_value(false), "Use euclidean distance calculation instead of OSRM for access and egress calculations"); options.add_options() ("osrmPort,osrmWalkPort,osrmWalkingPort", boost::program_options::value()->default_value("5000"), "osrm walking port"); options.add_options() @@ -43,6 +45,7 @@ namespace TrRouting { dataFetcherShortname = "cache"; cachePath = "cache"; cacheAllConnectionSets = false; + useEuclideanDistance = false; osrmWalkingPort = "5000"; osrmCyclingPort = "8000"; osrmDrivingPort = "7000"; @@ -78,6 +81,10 @@ namespace TrRouting { { cacheAllConnectionSets = variablesMap["cacheAllConnectionSets"].as(); } + if(variablesMap.count("useEuclideanDistance") == 1) + { + useEuclideanDistance = variablesMap["useEuclideanDistance"].as(); + } if(variablesMap.count("osrmWalkPort") == 1) { diff --git a/connection_scan_algorithm/src/transit_routing_http_server.cpp b/connection_scan_algorithm/src/transit_routing_http_server.cpp index 5521bb13..1064459d 100644 --- a/connection_scan_algorithm/src/transit_routing_http_server.cpp +++ b/connection_scan_algorithm/src/transit_routing_http_server.cpp @@ -29,6 +29,7 @@ #include "routing_result.hpp" #include "transit_data.hpp" #include "osrmgeofilter.hpp" +#include "euclideangeofilter.hpp" using namespace TrRouting; @@ -125,8 +126,18 @@ int main(int argc, char** argv) { //TODO We wanted to handle error in the constructor, but later part of this code expect a dataStatus // leaving as a todo DataStatus dataStatus = transitData.getDataStatus(); - OsrmGeoFilter geoFilter("walking", programOptions.osrmWalkingHost, programOptions.osrmWalkingPort); - Calculator calculator(transitData, geoFilter); + + // Selection which geofilter to use. OSRM is the default one. Euclidean mostly used for debugging and testing + GeoFilter *geoFilter = 0; + if (programOptions.useEuclideanDistance) { + geoFilter = new EuclideanGeoFilter(); + spdlog::info("Using Euclidean distance for access/egress node time/distance"); + } else { + geoFilter = new OsrmGeoFilter("walking", programOptions.osrmWalkingHost, programOptions.osrmWalkingPort); + spdlog::info("Using OSRM for access/egress node time/distance"); + } + + Calculator calculator(transitData, *geoFilter); //TODO, should this be in the constructor? calculator.initializeCalculationData(); spdlog::info("preparing server..."); From 9aaa35da78dfc69aa643aaf0f7dcc83eaab5e3f2 Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 31 Aug 2023 16:16:15 -0400 Subject: [PATCH 3/4] Check http status_code of OSRM Request Validate that the server returned an 200 OK before attempting to parse the OSRM data. The function will return an empty array if the response was not valid --- src/osrmgeofilter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/osrmgeofilter.cpp b/src/osrmgeofilter.cpp index 8ac08620..a3125307 100644 --- a/src/osrmgeofilter.cpp +++ b/src/osrmgeofilter.cpp @@ -57,7 +57,12 @@ namespace TrRouting { HttpClient client(host + ":" + port); auto s = client.request("GET", queryString); - //TODO CHeck status of s + if (s->status_code != "200 OK") { + spdlog::error("Error fetching OSRM data ({})", s->status_code); + //TODO We should throw an exception somehow here to invalidate the current calculation + // and returne an informative error code to the user + return accessibleNodesFootpaths; + } std::stringstream responseJsonSs; responseJsonSs << s->content.rdbuf(); From cf1956f5da514fcf55fa961eebd22cc4d6613bf5 Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 19 Oct 2023 15:25:03 -0400 Subject: [PATCH 4/4] Add a comment to explain why we do not directly call EuclideanGeoFilter in the OSRMGeoFilter, in the first filter pass --- src/osrmgeofilter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/osrmgeofilter.cpp b/src/osrmgeofilter.cpp index a3125307..94bcca1e 100644 --- a/src/osrmgeofilter.cpp +++ b/src/osrmgeofilter.cpp @@ -31,6 +31,9 @@ namespace TrRouting { std::string queryString = "/table/v1/" + mode + "/" + std::to_string(point.longitude) + "," + std::to_string(point.latitude); + // We first filter the nodes with euclidean distance using the common distance calculation + // to only send a subset of nodes to OSRM. We do not reuse the EuclideanGeoFilter directly, since + // we process the data differently here. (We directly compute the OSRM query.) for (auto &&[uuid,node] : nodes) { distanceMetersSquared = calculateNodeDistanceSquared(node.point.get(), point, lengthOfOneDegree);