diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp new file mode 100644 index 000000000..a530c2e30 --- /dev/null +++ b/include/engine/api/nearest_api.hpp @@ -0,0 +1,55 @@ +#ifndef ENGINE_API_NEAREST_API_HPP +#define ENGINE_API_NEAREST_API_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/nearest_parameters.hpp" + +#include "engine/api/json_factory.hpp" +#include "engine/phantom_node.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class NearestAPI final : public BaseAPI +{ + public: + NearestAPI(const datafacade::BaseDataFacade &facade_, const NearestParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } + + void MakeResponse(const std::vector> &phantom_nodes, util::json::Object& response) const + { + BOOST_ASSERT(phantom_nodes.size() == 1); + BOOST_ASSERT(parameters.coordinates.size() == 1); + + util::json::Array waypoints; + waypoints.values.resize(phantom_nodes.front().size()); + std::transform(phantom_nodes.front().begin(), + phantom_nodes.front().end(), waypoints.values.begin(), [this](const PhantomNodeWithDistance& phantom_with_distance) + { + auto waypoint = MakeWaypoint(parameters.coordinates.front(), phantom_with_distance.phantom_node); + waypoint.values["distance"] = phantom_with_distance.distance; + return waypoint; + }); + + response.values["code"] = "ok"; + response.values["waypoints"] = std::move(waypoints); + } + + const NearestParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index bd1552951..592a5079d 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -88,7 +88,6 @@ class BaseDataFacade const float max_distance, const int bearing, const int bearing_range) = 0; - virtual std::vector NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate, const float max_distance) = 0; @@ -96,21 +95,30 @@ class BaseDataFacade virtual std::vector NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) = 0; + const double max_distance, + const int bearing, + const int bearing_range) = 0; + virtual std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) = 0; + virtual std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results) = 0; + virtual std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, const double max_distance) = 0; virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate) = 0; - virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, const double max_distance) = 0; - virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, const double max_distance, const int bearing, const int bearing_range) = 0; - virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, const int bearing, diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index a27716684..e79c708e4 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -383,11 +383,38 @@ class InternalDataFacade final : public BaseDataFacade bearing, bearing_range); } + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results); + } + std::vector NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) override final + const double max_distance) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance); + } + + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) override final { if (!m_static_rtree.get()) { @@ -399,6 +426,23 @@ class InternalDataFacade final : public BaseDataFacade bearing_range); } + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance, + bearing, bearing_range); + } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, const double max_distance) override final { diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index b3061d4ab..6d3b967b2 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -235,7 +235,8 @@ class SharedDataFacade final : public BaseDataFacade } data_timestamp_ptr = static_cast( storage::makeSharedMemory(storage::CURRENT_REGIONS, - sizeof(storage::SharedDataTimestamp), false, false)->Ptr()); + sizeof(storage::SharedDataTimestamp), false, false) + ->Ptr()); CURRENT_LAYOUT = storage::LAYOUT_NONE; CURRENT_DATA = storage::DATA_NONE; CURRENT_TIMESTAMP = 0; @@ -306,8 +307,8 @@ class SharedDataFacade final : public BaseDataFacade LoadNames(); LoadCoreInformation(); - util::SimpleLogger().Write() - << "number of geometries: " << m_coordinate_list->size(); + util::SimpleLogger().Write() << "number of geometries: " + << m_coordinate_list->size(); for (unsigned i = 0; i < m_coordinate_list->size(); ++i) { if (!GetCoordinateOfNode(i).IsValid()) @@ -450,11 +451,38 @@ class SharedDataFacade final : public BaseDataFacade bearing, bearing_range); } + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results); + } + std::vector NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) override final + const double max_distance) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance); + } + + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) override final { if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) { @@ -466,6 +494,23 @@ class SharedDataFacade final : public BaseDataFacade bearing_range); } + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance, + bearing, bearing_range); + } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate) override final { @@ -523,7 +568,6 @@ class SharedDataFacade final : public BaseDataFacade input_coordinate, bearing, bearing_range); } - unsigned GetCheckSum() const override final { return m_check_sum; } unsigned GetNameIndexFromEdgeID(const unsigned id) const override final diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index c64cce12b..af5cab038 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -82,8 +82,8 @@ template class GeospatialQuery std::vector NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) + const int bearing, + const int bearing_range) { auto results = rtree.Nearest(input_coordinate, [this, bearing, bearing_range](const EdgeData &data) @@ -98,6 +98,67 @@ template class GeospatialQuery return MakePhantomNodes(input_coordinate, results); } + // Returns max_results nearest PhantomNodes in the given bearing range within the maximum distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) + { + auto results = rtree.Nearest(input_coordinate, + [this, bearing, bearing_range](const EdgeData &data) + { + return checkSegmentBearing(data, bearing, bearing_range); + }, + [max_results, max_distance](const std::size_t num_results, const double min_dist) + { + return num_results >= max_results || min_dist > max_distance; + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results) + { + auto results = rtree.Nearest(input_coordinate, + [](const EdgeData &) + { + return std::make_pair(true, true); + }, + [max_results](const std::size_t num_results, const double) + { + return num_results >= max_results; + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes in the given max distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate, + const unsigned max_results, + const double max_distance) + { + auto results = rtree.Nearest(input_coordinate, + [](const EdgeData &) + { + return std::make_pair(true, true); + }, + [max_results, max_distance](const std::size_t num_results, const double min_dist) + { + return num_results >= max_results || min_dist > max_distance; + }); + + return MakePhantomNodes(input_coordinate, results); + } + // Returns the nearest phantom node. If this phantom node is not from a big component // a second phantom node is return that is the nearest coordinate in a big component. std::pair NearestPhantomNodeWithAlternativeFromBigComponent( diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index 3bec1747b..c8fe930e0 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -150,6 +150,70 @@ class BasePlugin return phantom_nodes; } + std::vector> GetPhantomNodes(const api::BaseParameters ¶meters, unsigned number_of_results) + { + std::vector> phantom_nodes(parameters.coordinates.size()); + + const bool use_hints = !parameters.hints.empty(); + const bool use_bearings = !parameters.bearings.empty(); + const bool use_radiuses = !parameters.radiuses.empty(); + + BOOST_ASSERT(parameters.IsValid()); + for (const auto i : util::irange(0, parameters.coordinates.size())) + { + if (use_hints && parameters.hints[i] && + parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) + { + phantom_nodes[i].push_back(PhantomNodeWithDistance{ + parameters.hints[i]->phantom, + util::coordinate_calculation::haversineDistance( + parameters.coordinates[i], parameters.hints[i]->phantom.location), + }); + continue; + } + + if (use_bearings && parameters.bearings[i]) + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_nodes[i] = + facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, *parameters.radiuses[i], + parameters.bearings[i]->bearing, parameters.bearings[i]->range); + } + else + { + phantom_nodes[i] = + facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, parameters.bearings[i]->bearing, + parameters.bearings[i]->range); + } + } + else + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_nodes[i] = + facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, *parameters.radiuses[i]); + } + else + { + phantom_nodes[i] = + facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results); + } + } + + // we didn't found a fitting node, return error + if (phantom_nodes[i].empty()) + { + break; + } + } + return phantom_nodes; + } + std::vector GetPhantomNodes(const api::BaseParameters ¶meters) { std::vector phantom_node_pairs(parameters.coordinates.size()); diff --git a/include/server/api/nearest_parameter_grammar.hpp b/include/server/api/nearest_parameter_grammar.hpp index a21089da1..184467585 100644 --- a/include/server/api/nearest_parameter_grammar.hpp +++ b/include/server/api/nearest_parameter_grammar.hpp @@ -26,7 +26,12 @@ struct NearestParametersGrammar final : public BaseParametersGrammar NearestParametersGrammar() : BaseParametersGrammar(root_rule, parameters) { - root_rule = "TODO(daniel-j-h)"; + const auto set_number = [this](const unsigned number) + { + parameters.number_of_results = number; + }; + nearest_rule = (qi::lit("number=") >> qi::uint_)[set_number]; + root_rule = *(base_rule | nearest_rule); } engine::api::NearestParameters parameters; diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp index 7d7d179df..ffd2c6594 100644 --- a/src/engine/plugins/nearest.cpp +++ b/src/engine/plugins/nearest.cpp @@ -1,4 +1,6 @@ #include "engine/plugins/nearest.hpp" +#include "engine/api/nearest_parameters.hpp" +#include "engine/api/nearest_api.hpp" #include "engine/phantom_node.hpp" #include "util/integer_range.hpp" @@ -17,69 +19,29 @@ namespace plugins NearestPlugin::NearestPlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms, - util::json::Object &result) + util::json::Object &json_result) { BOOST_ASSERT(params.IsValid()); if (!CheckAllCoordinates(params.coordinates)) - return Error("invalid-options", "Coordinates are invalid", result); + return Error("InvalidOptions", "Coordinates are invalid", json_result); - if (params.bearings.size() > 0 && params.coordinates.size() != params.bearings.size()) - return Error("invalid-options", "Number of bearings does not match number of coordinates", - result); - - const auto &input_bearings = params.bearings; - auto number_of_results = static_cast(params.number_of_results); - - /* TODO(daniel-j-h): bearing range? - const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; - const int range = input_bearings.size() > 0 - ? (input_bearings.front().second ? *input_bearings.front().second : 10) - : 180; - auto phantom_node_vector = - facade.NearestPhantomNodes(params.coordinates.front(), number_of_results, bearing, range); - - if (phantom_node_vector.empty()) + if (params.coordinates.size() != 1) { - result.values["status_message"] = - std::string("Could not find a matching segments for coordinate"); - return Status::NoSegment; + return Error("TooBig", "Only one input coordinate is supported", json_result); } - else - { - result.values["status_message"] = "Found nearest edge"; - if (number_of_results > 1) - { - util::json::Array results; - auto vector_length = phantom_node_vector.size(); - for (const auto i : - util::irange(0, std::min(number_of_results, vector_length))) - { - const auto &node = phantom_node_vector[i].phantom_node; - util::json::Array json_coordinate; - util::json::Object result; - json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); - json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); - result.values["mapped coordinate"] = json_coordinate; - result.values["name"] = facade.get_name_for_id(node.name_id); - results.values.push_back(result); - } - result.values["results"] = results; - } - else - { - util::json::Array json_coordinate; - json_coordinate.values.push_back(phantom_node_vector.front().phantom_node.location.lat / - COORDINATE_PRECISION); - json_coordinate.values.push_back(phantom_node_vector.front().phantom_node.location.lon / - COORDINATE_PRECISION); - result.values["mapped_coordinate"] = json_coordinate; - result.values["name"] = - facade.get_name_for_id(phantom_node_vector.front().phantom_node.name_id); - } + auto phantom_nodes = GetPhantomNodes(params, params.number_of_results); + + if (phantom_nodes.front().size() == 0) + { + return Error("NoSegment", "Could not find a matching segments for coordinate", json_result); } - */ + BOOST_ASSERT(phantom_nodes.front().size() > 0); + + api::NearestAPI nearest_api(facade, params); + nearest_api.MakeResponse(phantom_nodes, json_result); + return Status::Ok; } } diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp index 0fdd6e1bf..067bd70aa 100644 --- a/src/server/service/nearest_service.cpp +++ b/src/server/service/nearest_service.cpp @@ -1,4 +1,5 @@ #include "server/service/nearest_service.hpp" +#include "server/service/utils.hpp" #include "engine/api/nearest_parameters.hpp" #include "server/api/parameters_parser.hpp" @@ -14,11 +15,58 @@ namespace server namespace service { +namespace +{ +std::string getWrongOptionHelp(const engine::api::NearestParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + engine::Status NearestService::RunQuery(std::vector coordinates, std::string &options, util::json::Object &result) { - // TODO(daniel-j-h) + auto options_iterator = options.begin(); + auto parameters = + api::parseParameters(options_iterator, options.end()); + if (!parameters || options_iterator != options.end()) + { + const auto position = std::distance(options.begin(), options_iterator); + result.values["code"] = "invalid-options"; + result.values["message"] = + "Options string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + + BOOST_ASSERT(parameters); + parameters->coordinates = std::move(coordinates); + + if (!parameters->IsValid()) + { + result.values["code"] = "invalid-options"; + result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Nearest(*parameters, result); return Status::Error; } }