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/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/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/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/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..1064459d 100644 --- a/connection_scan_algorithm/src/transit_routing_http_server.cpp +++ b/connection_scan_algorithm/src/transit_routing_http_server.cpp @@ -27,8 +27,9 @@ #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" +#include "euclideangeofilter.hpp" using namespace TrRouting; @@ -108,13 +109,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 +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(); - - Calculator calculator(transitData); + + // 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..."); 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..94bcca1e --- /dev/null +++ b/src/osrmgeofilter.cpp @@ -0,0 +1,102 @@ +#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); + + // 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); + + 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); + + 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(); + 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();