Add viaroute suport for new API

This commit is contained in:
Patrick Niklaus 2016-01-28 16:28:44 +01:00
parent 54ee76bcef
commit f3e72623e9
87 changed files with 3352 additions and 2099 deletions

View File

@ -45,7 +45,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND}
COMMENT "Configuring revision fingerprint" COMMENT "Configuring revision fingerprint"
VERBATIM) VERBATIM)
add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests) add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests server-tests)
add_custom_target(benchmarks DEPENDS rtree-bench) add_custom_target(benchmarks DEPENDS rtree-bench)
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework) set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework)
@ -63,6 +63,7 @@ file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp) file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp)
file(GLOB EngineTestsGlob unit_tests/engine/*.cpp) file(GLOB EngineTestsGlob unit_tests/engine/*.cpp)
file(GLOB UtilTestsGlob unit_tests/util/*.cpp) file(GLOB UtilTestsGlob unit_tests/util/*.cpp)
file(GLOB ServerTestsGlob unit_tests/server/*.cpp)
file(GLOB IOTestsGlob unit_tests/io/*.cpp) file(GLOB IOTestsGlob unit_tests/io/*.cpp)
add_library(UTIL OBJECT ${UtilGlob}) add_library(UTIL OBJECT ${UtilGlob})
@ -88,6 +89,7 @@ add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>) add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>)
add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>) add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $<TARGET_OBJECTS:UTIL>) add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $<TARGET_OBJECTS:UTIL>)
add_executable(server-tests EXCLUDE_FROM_ALL unit_tests/server_tests.cpp ${ServerTestsGlob} $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:SERVER>)
# Benchmarks # Benchmarks
add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $<TARGET_OBJECTS:UTIL>) add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $<TARGET_OBJECTS:UTIL>)
@ -332,6 +334,7 @@ target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES})
target_link_libraries(osrm_store ${STORAGE_LIBRARIES}) target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
# Tests # Tests
target_link_libraries(engine-tests ${ENGINE_LIBRARIES}) target_link_libraries(engine-tests ${ENGINE_LIBRARIES})
target_link_libraries(server-tests osrm ${Boost_LIBRARIES})
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES}) target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES})
target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES}) target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES})
target_link_libraries(util-tests ${UTIL_LIBRARIES}) target_link_libraries(util-tests ${UTIL_LIBRARIES})

View File

@ -0,0 +1,70 @@
#ifndef ENGINE_API_BASE_API_HPP
#define ENGINE_API_BASE_API_HPP
#include "engine/api/base_parameters.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/hint.hpp"
#include <boost/assert.hpp>
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
namespace detail
{
template <typename ChildT> class BaseAPI_
{
public:
BaseAPI_(const datafacade::BaseDataFacade &facade_, const BaseParameters &parameters_)
: facade(facade_), parameters(parameters_)
{
}
virtual util::json::Array
MakeWaypoints(const std::vector<PhantomNodes> &segment_end_coordinates) const
{
BOOST_ASSERT(parameters.coordinates.size() > 0);
BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1);
util::json::Array waypoints;
waypoints.values.resize(parameters.coordinates.size());
waypoints.values[0] = MakeWaypoint(parameters.coordinates.front(),
segment_end_coordinates.front().source_phantom);
auto coord_iter = std::next(parameters.coordinates.begin());
auto out_iter = std::next(waypoints.values.begin());
for (const auto &phantoms : segment_end_coordinates)
{
*out_iter++ = MakeWaypoint(*coord_iter++, phantoms.target_phantom);
}
return waypoints;
}
protected:
virtual util::json::Object MakeWaypoint(const util::FixedPointCoordinate input_coordinate,
const PhantomNode &phantom) const
{
return json::makeWaypoint(phantom.location, facade.get_name_for_id(phantom.name_id),
Hint{input_coordinate, phantom, facade.GetCheckSum()});
}
const datafacade::BaseDataFacade &facade;
const BaseParameters &parameters;
};
}
// Only expose non-templated version
using BaseAPI = detail::BaseAPI_<std::true_type>;
}
}
}
#endif

View File

@ -0,0 +1,51 @@
#ifndef ENGINE_API_BASE_PARAMETERS_HPP
#define ENGINE_API_BASE_PARAMETERS_HPP
#include "engine/hint.hpp"
#include "util/coordinate.hpp"
#include <boost/optional.hpp>
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct BaseParameters
{
struct Bearing
{
short bearing;
short range;
};
std::vector<util::FixedPointCoordinate> coordinates;
std::vector<boost::optional<Hint>> hints;
std::vector<boost::optional<double>> radiuses;
std::vector<boost::optional<Bearing>> bearings;
// FIXME add validation for invalid bearing values
bool IsValid() const
{
return (hints.empty() || hints.size() == coordinates.size()) &&
(bearings.empty() || bearings.size() == coordinates.size()) &&
(radiuses.empty() || radiuses.size() == coordinates.size());
}
};
inline bool operator==(const BaseParameters::Bearing lhs, const BaseParameters::Bearing rhs)
{
return lhs.bearing == rhs.bearing && lhs.range == rhs.range;
}
inline bool operator!=(const BaseParameters::Bearing lhs, const BaseParameters::Bearing rhs)
{
return !(lhs == rhs);
}
}
}
}
#endif // ROUTE_PARAMETERS_HPP

View File

@ -0,0 +1,90 @@
#ifndef ENGINE_RESPONSE_OBJECTS_HPP_
#define ENGINE_RESPONSE_OBJECTS_HPP_
#include "extractor/turn_instructions.hpp"
#include "extractor/travel_mode.hpp"
#include "engine/polyline_compressor.hpp"
#include "util/coordinate.hpp"
#include "util/json_container.hpp"
#include <boost/optional.hpp>
#include <string>
#include <algorithm>
#include <vector>
namespace osrm
{
namespace engine
{
struct Hint;
namespace guidance
{
class RouteLeg;
class RouteStep;
class StepManeuver;
class Route;
class LegGeometry;
}
namespace api
{
namespace json
{
namespace detail
{
std::string instructionToString(extractor::TurnInstruction instruction);
util::json::Array coordinateToLonLat(const FixedPointCoordinate &coordinate);
std::string modeToString(const extractor::TravelMode mode);
} // namespace detail
template <typename ForwardIter> util::json::String makePolyline(ForwardIter begin, ForwardIter end)
{
util::json::String polyline;
polyline.value = encodePolyline(begin, end);
return polyline;
}
template <typename ForwardIter>
util::json::Object makeGeoJSONLineString(ForwardIter begin, ForwardIter end)
{
util::json::Object geojson;
geojson.values["type"] = "LineString";
util::json::Array coordinates;
std::transform(begin, end, std::back_inserter(coordinates.values),
[](const util::FixedPointCoordinate loc)
{
return detail::coordinateToLonLat(loc);
});
geojson.values["coordinates"] = std::move(coordinates);
return geojson;
}
util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver);
util::json::Object makeRouteStep(guidance::RouteStep &&step,
boost::optional<util::json::Value> geometry);
util::json::Object makeRoute(const guidance::Route &route,
util::json::Array &&legs,
boost::optional<util::json::Value> geometry);
util::json::Object
makeWaypoint(const FixedPointCoordinate location, std::string &&way_name, const Hint &hint);
util::json::Object makeRouteLeg(guidance::RouteLeg &&leg, util::json::Array &&steps);
util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> &&legs,
const std::vector<guidance::LegGeometry> &leg_geometries);
}
}
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_

View File

@ -0,0 +1,25 @@
#ifndef ENGINE_API_MATCH_PARAMETERS_HPP
#define ENGINE_API_MATCH_PARAMETERS_HPP
#include "engine/api/route_parameters.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct MatchParameters : public RouteParameters
{
std::vector<unsigned> timestamps;
bool IsValid() const;
};
}
}
}
#endif

View File

@ -0,0 +1,23 @@
#ifndef ENGINE_API_NEAREST_PARAMETERS_HPP
#define ENGINE_API_NEAREST_PARAMETERS_HPP
#include "engine/api/base_parameters.hpp"
namespace osrm
{
namespace engine
{
namespace api
{
struct NearestParameters : public BaseParameters
{
unsigned number_of_results;
bool IsValid() const;
};
}
}
}
#endif // ROUTE_PARAMETERS_HPP

View File

@ -0,0 +1,135 @@
#ifndef ENGINE_API_ROUTE_HPP
#define ENGINE_API_ROUTE_HPP
#include "engine/api/base_api.hpp"
#include "engine/api/route_parameters.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/guidance/assemble_leg.hpp"
#include "engine/guidance/assemble_route.hpp"
#include "engine/guidance/assemble_geometry.hpp"
#include "engine/guidance/assemble_overview.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/internal_route_result.hpp"
#include "util/integer_range.hpp"
namespace osrm
{
namespace engine
{
namespace api
{
namespace detail
{
template <typename ChildT> class RouteAPI_ : public BaseAPI_<RouteAPI_<ChildT>>
{
using BaseT = BaseAPI_<RouteAPI_<ChildT>>;
public:
RouteAPI_(const datafacade::BaseDataFacade &facade_, const RouteParameters &parameters_)
: BaseT(facade_, parameters_), parameters(parameters_)
{
}
virtual void MakeResponse(const InternalRouteResult &raw_route,
util::json::Object &response) const
{
auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL;
util::json::Array routes;
routes.values.resize(number_of_routes);
routes.values[0] =
MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments,
raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse);
if (raw_route.has_alternative())
{
std::vector<std::vector<PathData>> wrapped_leg(1);
wrapped_leg.front() = std::move(raw_route.unpacked_alternative);
routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg,
raw_route.alt_source_traversed_in_reverse,
raw_route.alt_target_traversed_in_reverse);
}
response.values["waypoints"] = BaseT::MakeWaypoints(raw_route.segment_end_coordinates);
response.values["routes"] = std::move(routes);
response.values["code"] = "ok";
}
protected:
template <typename ForwardIter>
util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const
{
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
{
return json::makePolyline(begin, end);
}
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
return json::makeGeoJSONLineString(begin, end);
}
virtual util::json::Object
MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
const std::vector<std::vector<PathData>> &unpacked_path_segments,
const std::vector<bool> &source_traversed_in_reverse,
const std::vector<bool> &target_traversed_in_reverse) const
{
std::vector<guidance::RouteLeg> legs;
std::vector<guidance::LegGeometry> leg_geometries;
auto number_of_legs = segment_end_coordinates.size();
legs.reserve(number_of_legs);
leg_geometries.reserve(number_of_legs);
for (auto idx : util::irange(0UL, number_of_legs))
{
const auto &phantoms = segment_end_coordinates[idx];
const auto &path_data = unpacked_path_segments[idx];
const bool reversed_source = source_traversed_in_reverse[idx];
const bool reversed_target = target_traversed_in_reverse[idx];
auto leg_geometry = guidance::assembleGeometry(
BaseT::facade, path_data, phantoms.source_phantom, phantoms.target_phantom);
auto leg = guidance::assembleLeg(BaseT::facade, path_data, leg_geometry, phantoms.source_phantom,
phantoms.target_phantom, reversed_source, reversed_target);
if (parameters.steps)
{
leg.steps = guidance::assembleSteps(BaseT::facade,
path_data, leg_geometry, phantoms.source_phantom, phantoms.target_phantom,
reversed_source, reversed_target);
}
leg_geometries.push_back(std::move(leg_geometry));
legs.push_back(std::move(leg));
}
auto route = guidance::assembleRoute(legs);
boost::optional<util::json::Value> json_overview;
if (parameters.overview != RouteParameters::OverviewType::False)
{
const auto use_simplification =
parameters.overview == RouteParameters::OverviewType::Simplified;
BOOST_ASSERT(use_simplification ||
parameters.overview == RouteParameters::OverviewType::Full);
auto overview = guidance::assembleOverview(leg_geometries, use_simplification);
json_overview = MakeGeometry(overview.begin(), overview.end());
}
return json::makeRoute(route, json::makeRouteLegs(std::move(legs), leg_geometries),
std::move(json_overview));
}
const RouteParameters &parameters;
};
}
// Expose non-templated version
using RouteAPI = detail::RouteAPI_<std::true_type>;
}
}
}
#endif

View File

@ -0,0 +1,58 @@
#ifndef ENGINE_API_ROUTE_PARAMETERS_HPP
#define ENGINE_API_ROUTE_PARAMETERS_HPP
#include "engine/api/base_parameters.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct RouteParameters : public BaseParameters
{
enum class GeometriesType
{
Polyline,
GeoJSON
};
enum class OverviewType
{
Simplified,
Full,
False
};
RouteParameters() = default;
template<typename... Args>
RouteParameters(const bool steps_,
const bool alternative_,
const GeometriesType geometries_,
const OverviewType overview_,
std::vector<boost::optional<bool>> uturns_, Args... args_)
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternative{alternative_}, geometries{geometries_},
overview{overview_}, uturns{std::move(uturns_)}
{
}
bool steps = true;
bool alternative = true;
GeometriesType geometries = GeometriesType::Polyline;
OverviewType overview = OverviewType::Simplified;
std::vector<boost::optional<bool>> uturns;
bool IsValid() const
{
return coordinates.size() >= 2 && BaseParameters::IsValid() &&
(uturns.empty() || uturns.size() == coordinates.size());
}
};
}
}
}
#endif

View File

@ -0,0 +1,27 @@
#ifndef ENGINE_API_TABLE_PARAMETERS_HPP
#define ENGINE_API_TABLE_PARAMETERS_HPP
#include "engine/api/base_parameters.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct TableParameters : public BaseParameters
{
std::vector<bool> is_source;
std::vector<bool> is_destination;
bool IsValid() const;
};
}
}
}
#endif // ROUTE_PARAMETERS_HPP

View File

@ -0,0 +1,24 @@
#ifndef ENGINE_API_TRIP_PARAMETERS_HPP
#define ENGINE_API_TRIP_PARAMETERS_HPP
#include "engine/api/route_parameters.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct TripParameters : public RouteParameters
{
bool IsValid() const;
};
}
}
}
#endif

View File

@ -1,299 +0,0 @@
#ifndef ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
#define ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
#include "guidance/segment_list.hpp"
#include "guidance/textual_route_annotation.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/object_encoder.hpp"
#include "engine/phantom_node.hpp"
#include "engine/polyline_formatter.hpp"
#include "engine/route_name_extraction.hpp"
#include "engine/segment_information.hpp"
#include "extractor/turn_instructions.hpp"
#include "osrm/coordinate.hpp"
#include "osrm/json_container.hpp"
#include "osrm/route_parameters.hpp"
#include "util/integer_range.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
#include <cstdint>
#include <cstddef>
#include <cmath>
#include <limits>
#include <string>
#include <utility>
#include <vector>
namespace osrm
{
namespace engine
{
namespace detail
{
struct Segment
{
uint32_t name_id;
int32_t length;
std::size_t position;
};
} // namespace detail
template <typename DataFacadeT> class ApiResponseGenerator
{
public:
using DataFacade = DataFacadeT;
using Segments = guidance::SegmentList<DataFacade>;
using Segment = detail::Segment;
ApiResponseGenerator(DataFacade *facade);
// This runs a full annotation, according to config.
// The output is tailored to the viaroute plugin.
void DescribeRoute(const RouteParameters &config,
const InternalRouteResult &raw_route,
util::json::Object &json_result);
// The following functions allow access to the different parts of the Describe Route
// functionality.
// For own responses, they can be used to generate only subsets of the information.
// In the normal situation, Describe Route is the desired usecase.
// generate an overview of a raw route
util::json::Object SummarizeRoute(const InternalRouteResult &raw_route,
const Segments &segment_list) const;
// create an array containing all via-points/-indices used in the query
util::json::Array ListViaPoints(const InternalRouteResult &raw_route) const;
util::json::Array ListViaIndices(const Segments &segment_list) const;
util::json::Value GetGeometry(const bool return_encoded, const Segments &segments) const;
// TODO this dedicated creation seems unnecessary? Only used for route names
std::vector<Segment> BuildRouteSegments(const Segments &segment_list) const;
// adds checksum and locations
util::json::Object BuildHintData(const InternalRouteResult &raw_route) const;
private:
// data access to translate ids back into names
DataFacade *facade;
};
template <typename DataFacadeT>
ApiResponseGenerator<DataFacadeT>::ApiResponseGenerator(DataFacadeT *facade_)
: facade(facade_)
{
}
template <typename DataFacadeT>
void ApiResponseGenerator<DataFacadeT>::DescribeRoute(const RouteParameters &config,
const InternalRouteResult &raw_route,
util::json::Object &json_result)
{
if (!raw_route.is_valid())
{
return;
}
const constexpr bool ALLOW_SIMPLIFICATION = true;
const constexpr bool EXTRACT_ROUTE = false;
const constexpr bool EXTRACT_ALTERNATIVE = true;
Segments segment_list(raw_route, EXTRACT_ROUTE, config.zoom_level, ALLOW_SIMPLIFICATION,
facade);
json_result.values["route_summary"] = SummarizeRoute(raw_route, segment_list);
json_result.values["via_points"] = ListViaPoints(raw_route);
json_result.values["via_indices"] = ListViaIndices(segment_list);
if (config.geometry)
{
json_result.values["route_geometry"] = GetGeometry(config.compression, segment_list);
}
if (config.print_instructions)
{
json_result.values["route_instructions"] =
guidance::AnnotateRoute(segment_list.Get(), facade);
}
RouteNames route_names;
if (raw_route.has_alternative())
{
Segments alternate_segment_list(raw_route, EXTRACT_ALTERNATIVE, config.zoom_level,
ALLOW_SIMPLIFICATION, facade);
// Alternative Route Summaries are stored in an array to (down the line) allow multiple
// alternatives
util::json::Array json_alternate_route_summary_array;
json_alternate_route_summary_array.values.emplace_back(
SummarizeRoute(raw_route, alternate_segment_list));
json_result.values["alternative_summaries"] = json_alternate_route_summary_array;
json_result.values["alternative_indices"] = ListViaIndices(alternate_segment_list);
if (config.geometry)
{
auto alternate_geometry_string =
GetGeometry(config.compression, alternate_segment_list);
util::json::Array json_alternate_geometries_array;
json_alternate_geometries_array.values.emplace_back(
std::move(alternate_geometry_string));
json_result.values["alternative_geometries"] = json_alternate_geometries_array;
}
if (config.print_instructions)
{
util::json::Array json_alternate_annotations_array;
json_alternate_annotations_array.values.emplace_back(
guidance::AnnotateRoute(alternate_segment_list.Get(), facade));
json_result.values["alternative_instructions"] = json_alternate_annotations_array;
}
// generate names for both the main path and the alternative route
auto path_segments = BuildRouteSegments(segment_list);
auto alternate_segments = BuildRouteSegments(alternate_segment_list);
route_names = extractRouteNames(path_segments, alternate_segments, facade);
util::json::Array json_alternate_names_array;
util::json::Array json_alternate_names;
json_alternate_names.values.push_back(route_names.alternative_path_name_1);
json_alternate_names.values.push_back(route_names.alternative_path_name_2);
json_alternate_names_array.values.emplace_back(std::move(json_alternate_names));
json_result.values["alternative_names"] = json_alternate_names_array;
json_result.values["found_alternative"] = util::json::True();
}
else
{
json_result.values["found_alternative"] = util::json::False();
// generate names for the main route on its own
auto path_segments = BuildRouteSegments(segment_list);
std::vector<detail::Segment> alternate_segments;
route_names = extractRouteNames(path_segments, alternate_segments, facade);
}
util::json::Array json_route_names;
json_route_names.values.push_back(route_names.shortest_path_name_1);
json_route_names.values.push_back(route_names.shortest_path_name_2);
json_result.values["route_name"] = json_route_names;
json_result.values["hint_data"] = BuildHintData(raw_route);
}
template <typename DataFacadeT>
util::json::Object
ApiResponseGenerator<DataFacadeT>::SummarizeRoute(const InternalRouteResult &raw_route,
const Segments &segment_list) const
{
util::json::Object json_route_summary;
if (!raw_route.segment_end_coordinates.empty())
{
const auto start_name_id = raw_route.segment_end_coordinates.front().source_phantom.name_id;
json_route_summary.values["start_point"] = facade->get_name_for_id(start_name_id);
const auto destination_name_id =
raw_route.segment_end_coordinates.back().target_phantom.name_id;
json_route_summary.values["end_point"] = facade->get_name_for_id(destination_name_id);
}
json_route_summary.values["total_time"] = segment_list.GetDuration();
json_route_summary.values["total_distance"] = segment_list.GetDistance();
return json_route_summary;
}
template <typename DataFacadeT>
util::json::Array
ApiResponseGenerator<DataFacadeT>::ListViaPoints(const InternalRouteResult &raw_route) const
{
util::json::Array json_via_points_array;
util::json::Array json_first_coordinate;
json_first_coordinate.values.emplace_back(
raw_route.segment_end_coordinates.front().source_phantom.location.lat /
COORDINATE_PRECISION);
json_first_coordinate.values.emplace_back(
raw_route.segment_end_coordinates.front().source_phantom.location.lon /
COORDINATE_PRECISION);
json_via_points_array.values.emplace_back(std::move(json_first_coordinate));
for (const PhantomNodes &nodes : raw_route.segment_end_coordinates)
{
std::string tmp;
util::json::Array json_coordinate;
json_coordinate.values.emplace_back(nodes.target_phantom.location.lat /
COORDINATE_PRECISION);
json_coordinate.values.emplace_back(nodes.target_phantom.location.lon /
COORDINATE_PRECISION);
json_via_points_array.values.emplace_back(std::move(json_coordinate));
}
return json_via_points_array;
}
template <typename DataFacadeT>
util::json::Array
ApiResponseGenerator<DataFacadeT>::ListViaIndices(const Segments &segment_list) const
{
util::json::Array via_indices;
via_indices.values.insert(via_indices.values.end(), segment_list.GetViaIndices().begin(),
segment_list.GetViaIndices().end());
return via_indices;
}
template <typename DataFacadeT>
util::json::Value ApiResponseGenerator<DataFacadeT>::GetGeometry(const bool return_encoded,
const Segments &segments) const
{
if (return_encoded)
return polylineEncodeAsJSON(segments.Get());
else
return polylineUnencodedAsJSON(segments.Get());
}
template <typename DataFacadeT>
std::vector<detail::Segment>
ApiResponseGenerator<DataFacadeT>::BuildRouteSegments(const Segments &segment_list) const
{
std::vector<detail::Segment> result;
for (const auto &segment : segment_list.Get())
{
const auto current_turn = segment.turn_instruction;
if (extractor::isTurnNecessary(current_turn) &&
(extractor::TurnInstruction::EnterRoundAbout != current_turn))
{
detail::Segment seg = {segment.name_id,
static_cast<int32_t>(segment.length),
static_cast<std::size_t>(result.size())};
result.emplace_back(std::move(seg));
}
}
return result;
}
template <typename DataFacadeT>
util::json::Object
ApiResponseGenerator<DataFacadeT>::BuildHintData(const InternalRouteResult &raw_route) const
{
util::json::Object json_hint_object;
json_hint_object.values["checksum"] = facade->GetCheckSum();
util::json::Array json_location_hint_array;
std::string hint;
for (const auto i : util::irange<std::size_t>(0, raw_route.segment_end_coordinates.size()))
{
hint = encodeBase64(raw_route.segment_end_coordinates[i].source_phantom);
json_location_hint_array.values.push_back(std::move(hint));
}
hint = encodeBase64(raw_route.segment_end_coordinates.back().target_phantom);
json_location_hint_array.values.emplace_back(std::move(hint));
json_hint_object.values["locations"] = json_location_hint_array;
return json_hint_object;
}
template <typename DataFacadeT>
ApiResponseGenerator<DataFacadeT> MakeApiResponseGenerator(DataFacadeT *facade)
{
return ApiResponseGenerator<DataFacadeT>(facade);
}
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_

View File

@ -5,6 +5,7 @@
#include "extractor/edge_based_node.hpp" #include "extractor/edge_based_node.hpp"
#include "extractor/external_memory_node.hpp" #include "extractor/external_memory_node.hpp"
#include "contractor/query_edge.hpp"
#include "engine/phantom_node.hpp" #include "engine/phantom_node.hpp"
#include "extractor/turn_instructions.hpp" #include "extractor/turn_instructions.hpp"
#include "util/integer_range.hpp" #include "util/integer_range.hpp"
@ -29,11 +30,11 @@ namespace datafacade
using EdgeRange = util::range<EdgeID>; using EdgeRange = util::range<EdgeID>;
template <class EdgeDataT> class BaseDataFacade class BaseDataFacade
{ {
public: public:
using EdgeData = contractor::QueryEdge::EdgeData;
using RTreeLeaf = extractor::EdgeBasedNode; using RTreeLeaf = extractor::EdgeBasedNode;
using EdgeData = EdgeDataT;
BaseDataFacade() {} BaseDataFacade() {}
virtual ~BaseDataFacade() {} virtual ~BaseDataFacade() {}
@ -46,7 +47,7 @@ template <class EdgeDataT> class BaseDataFacade
virtual NodeID GetTarget(const EdgeID e) const = 0; virtual NodeID GetTarget(const EdgeID e) const = 0;
virtual const EdgeDataT &GetEdgeData(const EdgeID e) const = 0; virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
virtual EdgeID BeginEdges(const NodeID n) const = 0; virtual EdgeID BeginEdges(const NodeID n) const = 0;
@ -94,10 +95,22 @@ template <class EdgeDataT> class BaseDataFacade
const int bearing = 0, const int bearing = 0,
const int bearing_range = 180) = 0; const int bearing_range = 180) = 0;
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate) = 0;
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const double max_distance) = 0;
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent( virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const util::FixedPointCoordinate input_coordinate,
const int bearing = 0, const double max_distance,
const int bearing_range = 180) = 0; const int bearing,
const int bearing_range) = 0;
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing,
const int bearing_range) = 0;
virtual unsigned GetCheckSum() const = 0; virtual unsigned GetCheckSum() const = 0;

View File

@ -46,17 +46,17 @@ namespace engine
namespace datafacade namespace datafacade
{ {
template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacade<EdgeDataT> class InternalDataFacade final : public BaseDataFacade
{ {
private: private:
using super = BaseDataFacade<EdgeDataT>; using super = BaseDataFacade;
using QueryGraph = util::StaticGraph<typename super::EdgeData>; using QueryGraph = util::StaticGraph<typename super::EdgeData>;
using InputEdge = typename QueryGraph::InputEdge; using InputEdge = typename QueryGraph::InputEdge;
using RTreeLeaf = typename super::RTreeLeaf; using RTreeLeaf = typename super::RTreeLeaf;
using InternalRTree = using InternalRTree =
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, false>::vector, false>; util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, false>::vector, false>;
using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade<EdgeDataT>>; using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade>;
InternalDataFacade() {} InternalDataFacade() {}
@ -293,7 +293,7 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
EdgeDataT &GetEdgeData(const EdgeID e) const override final EdgeData &GetEdgeData(const EdgeID e) const override final
{ {
return m_query_graph->GetEdgeData(e); return m_query_graph->GetEdgeData(e);
} }
@ -386,10 +386,52 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
bearing_range); bearing_range);
} }
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const double max_distance) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent( std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const util::FixedPointCoordinate input_coordinate,
const int bearing = 0, const double max_distance,
const int bearing_range = 180) override final 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->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance, bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing,
const int bearing_range) override final
{ {
if (!m_static_rtree.get()) if (!m_static_rtree.get())
{ {

View File

@ -36,12 +36,11 @@ namespace engine
namespace datafacade namespace datafacade
{ {
template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<EdgeDataT> class SharedDataFacade final : public BaseDataFacade
{ {
private: private:
using EdgeData = EdgeDataT; using super = BaseDataFacade;
using super = BaseDataFacade<EdgeData>;
using QueryGraph = util::StaticGraph<EdgeData, true>; using QueryGraph = util::StaticGraph<EdgeData, true>;
using GraphNode = typename QueryGraph::NodeArrayEntry; using GraphNode = typename QueryGraph::NodeArrayEntry;
using GraphEdge = typename QueryGraph::EdgeArrayEntry; using GraphEdge = typename QueryGraph::EdgeArrayEntry;
@ -50,7 +49,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
using RTreeLeaf = typename super::RTreeLeaf; using RTreeLeaf = typename super::RTreeLeaf;
using SharedRTree = using SharedRTree =
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, true>::vector, true>; util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, true>::vector, true>;
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade<EdgeDataT>>; using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade>;
using TimeStampedRTreePair = std::pair<unsigned, std::shared_ptr<SharedRTree>>; using TimeStampedRTreePair = std::pair<unsigned, std::shared_ptr<SharedRTree>>;
using RTreeNode = typename SharedRTree::TreeNode; using RTreeNode = typename SharedRTree::TreeNode;
@ -333,7 +332,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
EdgeDataT &GetEdgeData(const EdgeID e) const override final EdgeData &GetEdgeData(const EdgeID e) const override final
{ {
return m_query_graph->GetEdgeData(e); return m_query_graph->GetEdgeData(e);
} }
@ -454,10 +453,52 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
bearing_range); bearing_range);
} }
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, 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->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent( std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const util::FixedPointCoordinate input_coordinate,
const int bearing = 0, const double max_distance,
const int bearing_range = 180) override final 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->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance, bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing,
const int bearing_range) override final
{ {
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{ {
@ -469,6 +510,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
input_coordinate, bearing, bearing_range); input_coordinate, bearing, bearing_range);
} }
unsigned GetCheckSum() const override final { return m_check_sum; } unsigned GetCheckSum() const override final { return m_check_sum; }
unsigned GetNameIndexFromEdgeID(const unsigned id) const override final unsigned GetNameIndexFromEdgeID(const unsigned id) const override final

View File

@ -1,7 +1,7 @@
#ifndef DOUGLAS_PEUCKER_HPP_ #ifndef DOUGLAS_PEUCKER_HPP_
#define DOUGLAS_PEUCKER_HPP_ #define DOUGLAS_PEUCKER_HPP_
#include "engine/segment_information.hpp" #include "util/coordinate.hpp"
#include <vector> #include <vector>
#include <iterator> #include <iterator>
@ -44,14 +44,16 @@ const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE =
// Input is vector of pairs. Each pair consists of the point information and a // Input is vector of pairs. Each pair consists of the point information and a
// bit indicating if the points is present in the generalization. // bit indicating if the points is present in the generalization.
// Note: points may also be pre-selected*/ // Note: points may also be pre-selected*/
void douglasPeucker(std::vector<SegmentInformation>::iterator begin, std::vector<util::FixedPointCoordinate>
std::vector<SegmentInformation>::iterator end, douglasPeucker(std::vector<util::FixedPointCoordinate>::const_iterator begin,
const unsigned zoom_level); std::vector<util::FixedPointCoordinate>::const_iterator end,
const unsigned zoom_level);
// Convenience range-based function // Convenience range-based function
inline void douglasPeucker(std::vector<SegmentInformation> &geometry, const unsigned zoom_level) inline std::vector<util::FixedPointCoordinate>
douglasPeucker(const std::vector<util::FixedPointCoordinate> &geometry, const unsigned zoom_level)
{ {
douglasPeucker(begin(geometry), end(geometry), zoom_level); return douglasPeucker(begin(geometry), end(geometry), zoom_level);
} }
} }
} }

View File

@ -1,10 +1,9 @@
#ifndef ENGINE_HPP #ifndef ENGINE_HPP
#define ENGINE_HPP #define ENGINE_HPP
#include "contractor/query_edge.hpp" #include "engine/status.hpp"
#include "storage/shared_barriers.hpp"
#include "osrm/json_container.hpp" #include "util/json_container.hpp"
#include "osrm/osrm.hpp"
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@ -13,11 +12,6 @@
namespace osrm namespace osrm
{ {
namespace storage
{
struct SharedBarriers;
}
namespace util namespace util
{ {
namespace json namespace json
@ -29,41 +23,43 @@ struct Object;
namespace engine namespace engine
{ {
struct EngineConfig; struct EngineConfig;
namespace api
{
struct RouteParameters; struct RouteParameters;
}
namespace plugins namespace plugins
{ {
class BasePlugin; class ViaRoutePlugin;
} }
namespace datafacade namespace datafacade
{ {
template <class EdgeDataT> class BaseDataFacade; class BaseDataFacade;
} }
class Engine final class Engine final
{ {
private:
using PluginMap = std::unordered_map<std::string, std::unique_ptr<plugins::BasePlugin>>;
public: public:
struct EngineLock
{
// will only be initialized if shared memory is used
storage::SharedBarriers barrier;
// decrease number of concurrent queries
void DecreaseQueryCount();
// increase number of concurrent queries
void IncreaseQueryCount();
};
Engine(EngineConfig &config_); Engine(EngineConfig &config_);
Engine(const Engine &) = delete; Engine(const Engine &) = delete;
Engine &operator=(const Engine &) = delete; Engine &operator=(const Engine &) = delete;
int RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result); Status Route(const api::RouteParameters &route_parameters, util::json::Object &json_result);
private: private:
void RegisterPlugin(plugins::BasePlugin *plugin); std::unique_ptr<EngineLock> lock;
PluginMap plugin_map; std::unique_ptr<plugins::ViaRoutePlugin> route_plugin;
// will only be initialized if shared memory is used std::unique_ptr<datafacade::BaseDataFacade> query_data_facade;
std::unique_ptr<storage::SharedBarriers> barrier;
// base class pointer to the objects
datafacade::BaseDataFacade<contractor::QueryEdge::EdgeData> *query_data_facade;
// decrease number of concurrent queries
void decrease_concurrent_query_count();
// increase number of concurrent queries
void increase_concurrent_query_count();
}; };
} }
} }

View File

@ -84,16 +84,86 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
// Returns the nearest phantom node. If this phantom node is not from a big component // 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. // a second phantom node is return that is the nearest coordinate in a big component.
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent( std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate, const util::FixedPointCoordinate input_coordinate, const double max_distance)
const int bearing = 0,
const int bearing_range = 180)
{ {
bool has_small_component = false; bool has_small_component = false;
bool has_big_component = false; bool has_big_component = false;
auto results = rtree.Nearest( auto results = rtree.Nearest(
input_coordinate, input_coordinate,
[this, bearing, bearing_range, &has_big_component, &has_small_component]( [&has_big_component, &has_small_component](const EdgeData &data)
const EdgeData &data) {
auto use_segment =
(!has_small_component || (!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
return use_directions;
},
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
{
return (num_results > 0 && has_big_component) || min_dist > max_distance;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
// 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<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate)
{
bool has_small_component = false;
bool has_big_component = false;
auto results =
rtree.Nearest(input_coordinate,
[&has_big_component, &has_small_component](const EdgeData &data)
{
auto use_segment = (!has_small_component ||
(!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
return use_directions;
},
[&has_big_component](const std::size_t num_results, const double)
{
return num_results > 0 && has_big_component;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
// 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<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing,
const int bearing_range)
{
bool has_small_component = false;
bool has_big_component = false;
auto results = rtree.Nearest(
input_coordinate,
[this, bearing, bearing_range, &has_big_component,
&has_small_component](const EdgeData &data)
{ {
auto use_segment = auto use_segment =
(!has_small_component || (!has_big_component && !data.component.is_tiny)); (!has_small_component || (!has_big_component && !data.component.is_tiny));
@ -126,6 +196,52 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
MakePhantomNode(input_coordinate, results.back()).phantom_node); MakePhantomNode(input_coordinate, results.back()).phantom_node);
} }
// 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<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const double max_distance,
const int bearing,
const int bearing_range)
{
bool has_small_component = false;
bool has_big_component = false;
auto results = rtree.Nearest(
input_coordinate,
[this, bearing, bearing_range, &has_big_component,
&has_small_component](const EdgeData &data)
{
auto use_segment =
(!has_small_component || (!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
if (use_segment)
{
use_directions = checkSegmentBearing(data, bearing, bearing_range);
if (use_directions.first || use_directions.second)
{
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
}
}
return use_directions;
},
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
{
return (num_results > 0 && has_big_component) || min_dist > max_distance;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() > 0);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
private: private:
std::vector<PhantomNodeWithDistance> std::vector<PhantomNodeWithDistance>
MakePhantomNodes(const util::FixedPointCoordinate input_coordinate, MakePhantomNodes(const util::FixedPointCoordinate input_coordinate,

View File

@ -0,0 +1,78 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/coordinate.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/travel_mode.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
// Extracts the geometry for each segment and calculates the traveled distance
// Combines the geometry form the phantom node with the PathData
// to the full route geometry.
//
// turn 0 1 2 3 4
// s...x...y...z...t
// |---|segment 0
// |---| segment 1
// |---| segment 2
// |---| segment 3
template <typename DataFacadeT>
LegGeometry assembleGeometry(const DataFacadeT &facade,
const std::vector<PathData> &leg_data,
const PhantomNode &source_node,
const PhantomNode &target_node)
{
LegGeometry geometry;
// segment 0 first and last
geometry.segment_offsets.push_back(0);
geometry.locations.push_back(source_node.location);
auto current_distance = 0.;
auto prev_coordinate = geometry.locations.front();
for (const auto &path_point : leg_data)
{
auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node);
current_distance +=
util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate);
if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn)
{
geometry.segment_distances.push_back(current_distance);
geometry.segment_offsets.push_back(geometry.locations.size());
current_distance = 0.;
}
prev_coordinate = coordinate;
geometry.locations.push_back(std::move(coordinate));
}
current_distance +=
util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location);
// segment leading to the target node
geometry.segment_distances.push_back(current_distance);
geometry.segment_offsets.push_back(geometry.locations.size());
geometry.locations.push_back(target_node.location);
BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1);
BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size());
return geometry;
}
}
}
}
#endif

View File

@ -0,0 +1,165 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
#define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
#include "engine/guidance/route_leg.hpp"
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/internal_route_result.hpp"
#include <vector>
#include <array>
#include <algorithm>
namespace osrm
{
namespace engine
{
namespace guidance
{
namespace detail
{
const constexpr std::size_t MAX_USED_SEGMENTS = 2;
struct NamedSegment
{
double duration;
std::uint32_t position;
std::uint32_t name_id;
};
template <std::size_t SegmentNumber>
std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
{
// merges segments with same name id
const auto collapse_segments = [](std::vector<NamedSegment> &segments)
{
auto out = segments.begin();
auto end = segments.end();
for (auto in = segments.begin(); in != end; ++in)
{
if (in->name_id == out->name_id)
{
out->duration += in->duration;
}
else
{
++out;
BOOST_ASSERT(out != end);
*out = *in;
}
}
return out;
};
std::vector<NamedSegment> segments(route_data.size());
std::uint32_t index = 0;
std::transform(
route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point)
{
return NamedSegment{point.duration_until_turn / 10.0, index++, point.name_id};
});
// this makes sure that the segment with the lowest position comes first
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.name_id < rhs.name_id ||
(lhs.name_id == rhs.name_id && lhs.position < rhs.position);
});
auto new_end = collapse_segments(segments);
segments.resize(new_end - segments.begin());
// sort descending
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.duration > rhs.duration;
});
// make sure the segments are sorted by position
segments.resize(std::min(segments.size(), SegmentNumber));
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.position < rhs.position;
});
std::array<std::uint32_t, SegmentNumber> summary;
std::fill(summary.begin(), summary.end(), 0);
std::transform(segments.begin(), segments.end(), summary.begin(),
[](const NamedSegment &segment)
{
return segment.name_id;
});
return summary;
}
}
template <typename DataFacadeT>
RouteLeg assembleLeg(const DataFacadeT &facade,
const std::vector<PathData> &route_data,
const LegGeometry &leg_geometry,
const PhantomNode &source_node,
const PhantomNode &target_node,
const bool source_traversed_in_reverse,
const bool target_traversed_in_reverse)
{
const auto source_duration =
(source_traversed_in_reverse ? source_node.GetReverseWeightPlusOffset()
: source_node.GetForwardWeightPlusOffset()) /
10.;
const auto target_duration =
(target_traversed_in_reverse ? target_node.GetReverseWeightPlusOffset()
: target_node.GetForwardWeightPlusOffset()) /
10.;
auto distance = std::accumulate(leg_geometry.segment_distances.begin(),
leg_geometry.segment_distances.end(), 0.);
auto duration = std::accumulate(route_data.begin(), route_data.end(), 0.,
[](const double sum, const PathData &data)
{
return sum + data.duration_until_turn;
}) /
10.;
// s
// |
// Given a route a---b---c where there is a right turn at b.
// |
// d
// |--t
// e
// (a, b, c) gets compressed to (a,c)
// (c, d, e) gets compressed to (c,e)
// The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration
// of (a,b,c)).
// The phantom node of s will contain:
// `forward_weight`: duration of (a,s)
// `forward_offset`: 0 (its the first segment)
// The phantom node of t will contain:
// `forward_weight`: duration of (d,t)
// `forward_offset`: duration of (c, d)
//
// The PathData will contain entries of b, c and d. But only c will contain
// a duration value since its the only point associated with a turn.
// As such we want to slice of the duration for (a,s) and add the duration for
// (c,d,t)
duration = duration - source_duration + target_duration;
auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(route_data);
BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0);
BOOST_ASSERT(summary_array.begin() != summary_array.end());
std::string summary =
std::accumulate(std::next(summary_array.begin()), summary_array.end(),
facade.get_name_for_id(summary_array.front()),
[&facade](std::string previous, const std::uint32_t name_id)
{
if (name_id != 0)
{
previous += ", " + facade.get_name_for_id(name_id);
}
return previous;
});
return RouteLeg{duration, distance, summary, {}};
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_

View File

@ -0,0 +1,22 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#include "engine/guidance/leg_geometry.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
std::vector<util::FixedPointCoordinate>
assembleOverview(const std::vector<LegGeometry> &leg_geometries, const bool use_simplification);
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif

View File

@ -0,0 +1,37 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
#include "engine/guidance/route_leg.hpp"
#include "engine/guidance/route.hpp"
#include <vector>
#include <algorithm>
namespace osrm
{
namespace engine
{
namespace guidance
{
inline Route assembleRoute(const std::vector<RouteLeg> &route_legs)
{
auto distance = std::accumulate(route_legs.begin(),
route_legs.end(), 0.,
[](const double sum, const RouteLeg &leg)
{
return sum + leg.distance;
});
auto duration = std::accumulate(route_legs.begin(), route_legs.end(), 0.,
[](const double sum, const RouteLeg &leg)
{
return sum + leg.duration;
});
return Route{duration, distance};
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif

View File

@ -0,0 +1,135 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/step_maneuver.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/coordinate.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/travel_mode.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
namespace detail
{
// FIXME move implementation to cpp
inline StepManeuver stepManeuverFromGeometry(const extractor::TurnInstruction instruction,
const LegGeometry &leg_geometry,
std::size_t segment_index)
{
auto turn_index = leg_geometry.BackIndex(segment_index);
BOOST_ASSERT(turn_index > 0);
BOOST_ASSERT(turn_index < leg_geometry.locations.size() - 1);
// TODO chose a bigger look-a-head to smooth complex geometry
const auto pre_turn_coordinate = leg_geometry.locations[turn_index - 1];
const auto turn_coordinate = leg_geometry.locations[turn_index];
const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
const double pre_turn_heading =
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
const double post_turn_heading =
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
return StepManeuver{turn_coordinate, pre_turn_heading, post_turn_heading, instruction};
}
}
template <typename DataFacadeT>
std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const std::vector<PathData> &leg_data,
const LegGeometry &leg_geometry,
const PhantomNode &source_node,
const PhantomNode &target_node,
const bool source_traversed_in_reverse,
const bool target_traversed_in_reverse)
{
const auto source_duration =
(source_traversed_in_reverse ? source_node.GetReverseWeightPlusOffset()
: source_node.GetForwardWeightPlusOffset()) /
10.;
const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode
: source_node.forward_travel_mode;
const auto target_duration =
(target_traversed_in_reverse ? target_node.GetReverseWeightPlusOffset()
: target_node.GetForwardWeightPlusOffset()) /
10.;
const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode
: target_node.forward_travel_mode;
const auto number_of_segments = leg_geometry.GetNumberOfSegments();
std::vector<RouteStep> steps;
steps.reserve(number_of_segments);
auto segment_index = 0;
if (leg_data.size() > 0)
{
StepManeuver maneuver = detail::stepManeuverFromGeometry(extractor::TurnInstruction::StartAtEndOfStreet,
leg_geometry, segment_index);
// PathData saves the information we need of the segment _before_ the turn,
// but a RouteStep is with regard to the segment after the turn.
// We need to skip the first segment because it is already covered by the
for (const auto &path_point : leg_data)
{
if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn)
{
auto name = facade.get_name_for_id(path_point.name_id);
const auto distance = leg_geometry.segment_distances[segment_index];
steps.push_back(RouteStep{path_point.name_id, std::move(name),
path_point.duration_until_turn / 10.0, distance,
path_point.travel_mode, maneuver,
leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1});
maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
leg_geometry, segment_index);
segment_index++;
}
}
const auto distance = leg_geometry.segment_distances[segment_index];
steps.push_back(RouteStep{target_node.name_id, facade.get_name_for_id(target_node.name_id),
target_duration, distance, target_mode, maneuver,
leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1});
}
else
{
//
// |-----s source_duration
// |-------------t target_duration
// x---*---*---*---z compressed edge
// |-------| duration
steps.push_back(RouteStep{
source_node.name_id, facade.get_name_for_id(source_node.name_id),
target_duration - source_duration, leg_geometry.segment_distances[segment_index],
source_mode,
StepManeuver{source_node.location, 0., 0., extractor::TurnInstruction::StartAtEndOfStreet},
leg_geometry.FrontIndex(segment_index), leg_geometry.BackIndex(segment_index) + 1});
}
BOOST_ASSERT(segment_index == number_of_segments - 1);
// This step has length zero, the only reason we need it is the target location
steps.push_back(RouteStep{
target_node.name_id, facade.get_name_for_id(target_node.name_id), 0., 0., target_mode,
StepManeuver{target_node.location, 0., 0., extractor::TurnInstruction::ReachedYourDestination},
leg_geometry.locations.size(), leg_geometry.locations.size()});
return steps;
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_

View File

@ -0,0 +1,54 @@
#ifndef ENGINE_GUIDANCE_LEG_GEOMETRY_HPP
#define ENGINE_GUIDANCE_LEG_GEOMETRY_HPP
#include "util/coordinate.hpp"
#include "util/integer_range.hpp"
#include <boost/assert.hpp>
#include <boost/range/iterator_range.hpp>
#include <vector>
#include <cstdlib>
namespace osrm
{
namespace engine
{
namespace guidance
{
// locations 0---1---2-...-n-1---n
// turns s x y t
// segment | 0 | 1 | 2 | senitel
// offsets 0 2 n-1 n
struct LegGeometry
{
std::vector<util::FixedPointCoordinate> locations;
// segment_offset[i] .. segment_offset[i+1] (inclusive)
// contains the geometry of segment i
std::vector<std::size_t> segment_offsets;
// length of the segment in meters
std::vector<double> segment_distances;
std::size_t FrontIndex(std::size_t segment_index) const
{
return segment_offsets[segment_index];
}
std::size_t BackIndex(std::size_t segment_index) const
{
return segment_offsets[segment_index + 1];
}
std::size_t GetNumberOfSegments() const
{
BOOST_ASSERT(segment_offsets.size() > 0);
return segment_offsets.size() - 1;
}
};
}
}
}
#endif

View File

@ -0,0 +1,18 @@
#ifndef ROUTE_HPP
#define ROUTE_HPP
namespace osrm {
namespace engine {
namespace guidance {
struct Route
{
double duration;
double distance;
};
}
}
}
#endif

View File

@ -0,0 +1,30 @@
#ifndef ROUTE_LEG_HPP
#define ROUTE_LEG_HPP
#include "engine/guidance/route_step.hpp"
#include <boost/optional.hpp>
#include <string>
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
struct RouteLeg
{
double duration;
double distance;
std::string summary;
std::vector<RouteStep> steps;
};
}
}
}
#endif

View File

@ -0,0 +1,39 @@
#ifndef ROUTE_STEP_HPP
#define ROUTE_STEP_HPP
#include "extractor/travel_mode.hpp"
#include "engine/guidance/step_maneuver.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
// Given the following turn from a,b to b,c over b:
// a --> b --> c
// this struct saves the information of the segment b,c.
// Notable exceptions are Departure and Arrival steps.
// Departue: s --> a --> b. Represents the segment s,a with location being s.
// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment.
struct RouteStep
{
unsigned name_id;
std::string way_name;
double duration;
double distance;
extractor::TravelMode mode;
StepManeuver maneuver;
// indices into the locations array stored the LegGeometry
std::size_t geometry_begin;
std::size_t geometry_end;
};
}
}
}
#endif

View File

@ -1,347 +0,0 @@
#ifndef ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
#define ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
#include "osrm/coordinate.hpp"
#include "engine/douglas_peucker.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "engine/segment_information.hpp"
#include "util/integer_range.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/for_each_pair.hpp"
#include "extractor/turn_instructions.hpp"
#include <boost/assert.hpp>
#include <cmath>
#include <cstdint>
#include <cstddef>
#include <limits>
#include <vector>
// transfers the internal edge based data structures to a more useable format
namespace osrm
{
namespace engine
{
namespace guidance
{
template <typename DataFacadeT> class SegmentList
{
public:
using DataFacade = DataFacadeT;
SegmentList(const InternalRouteResult &raw_route,
const bool extract_alternative,
const unsigned zoom_level,
const bool allow_simplification,
const DataFacade *facade);
const std::vector<std::uint32_t> &GetViaIndices() const;
std::uint32_t GetDistance() const;
std::uint32_t GetDuration() const;
const std::vector<SegmentInformation> &Get() const;
private:
void InitRoute(const PhantomNode &phantom_node, const bool traversed_in_reverse);
void AddLeg(const std::vector<PathData> &leg_data,
const PhantomNode &target_node,
const bool traversed_in_reverse,
const bool is_via_leg,
const DataFacade *facade);
void AppendSegment(const FixedPointCoordinate coordinate, const PathData &path_point);
void Finalize(const bool extract_alternative,
const InternalRouteResult &raw_route,
const unsigned zoom_level,
const bool allow_simplification);
// journey length in tenth of a second
std::uint32_t total_distance;
// journey distance in meter (TODO: verify)
std::uint32_t total_duration;
// segments that are required to keep
std::vector<std::uint32_t> via_indices;
// a list of node based segments
std::vector<SegmentInformation> segments;
};
template <typename DataFacadeT>
SegmentList<DataFacadeT>::SegmentList(const InternalRouteResult &raw_route,
const bool extract_alternative,
const unsigned zoom_level,
const bool allow_simplification,
const DataFacade *facade)
: total_distance(0), total_duration(0)
{
if (!raw_route.is_valid())
{
return;
}
if (extract_alternative)
{
BOOST_ASSERT(raw_route.has_alternative());
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
raw_route.alt_source_traversed_in_reverse.front());
AddLeg(raw_route.unpacked_alternative,
raw_route.segment_end_coordinates.back().target_phantom,
raw_route.alt_source_traversed_in_reverse.back(), false, facade);
}
else
{
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
raw_route.source_traversed_in_reverse.front());
for (std::size_t raw_index = 0; raw_index < raw_route.segment_end_coordinates.size();
++raw_index)
{
AddLeg(raw_route.unpacked_path_segments[raw_index],
raw_route.segment_end_coordinates[raw_index].target_phantom,
raw_route.target_traversed_in_reverse[raw_index],
raw_route.is_via_leg(raw_index), facade);
if (raw_route.is_via_leg(raw_index))
{
const auto &source_phantom =
raw_route.segment_end_coordinates[raw_index].target_phantom;
if (raw_route.target_traversed_in_reverse[raw_index] !=
raw_route.source_traversed_in_reverse[raw_index + 1])
{
bool traversed_in_reverse = raw_route.target_traversed_in_reverse[raw_index];
const extractor::TravelMode travel_mode =
(traversed_in_reverse ? source_phantom.backward_travel_mode
: source_phantom.forward_travel_mode);
const bool constexpr IS_NECESSARY = true;
const bool constexpr IS_VIA_LOCATION = true;
segments.emplace_back(source_phantom.location, source_phantom.name_id, 0, 0.f,
extractor::TurnInstruction::UTurn, IS_NECESSARY,
IS_VIA_LOCATION, travel_mode);
}
}
}
}
if (!allow_simplification)
{
// to prevent any simplifications, we mark all segments as necessary
for (auto &segment : segments)
{
segment.necessary = true;
}
}
Finalize(extract_alternative, raw_route, zoom_level, allow_simplification);
}
template <typename DataFacadeT>
void SegmentList<DataFacadeT>::InitRoute(const PhantomNode &node, const bool traversed_in_reverse)
{
const auto segment_duration =
(traversed_in_reverse ? node.reverse_weight : node.forward_weight);
const auto travel_mode =
(traversed_in_reverse ? node.backward_travel_mode : node.forward_travel_mode);
AppendSegment(node.location, PathData(0, node.name_id, extractor::TurnInstruction::HeadOn,
segment_duration, travel_mode));
}
template <typename DataFacadeT>
void SegmentList<DataFacadeT>::AddLeg(const std::vector<PathData> &leg_data,
const PhantomNode &target_node,
const bool traversed_in_reverse,
const bool is_via_leg,
const DataFacade *facade)
{
for (const auto &path_data : leg_data)
{
AppendSegment(facade->GetCoordinateOfNode(path_data.node), path_data);
}
const EdgeWeight segment_duration =
(traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight);
const extractor::TravelMode travel_mode =
(traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode);
const bool constexpr IS_NECESSARY = true;
const bool constexpr IS_VIA_LOCATION = true;
segments.emplace_back(target_node.location, target_node.name_id, segment_duration, 0.f,
is_via_leg ? extractor::TurnInstruction::ReachViaLocation
: extractor::TurnInstruction::NoTurn,
IS_NECESSARY, IS_VIA_LOCATION, travel_mode);
}
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDistance() const
{
return total_distance;
}
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDuration() const
{
return total_duration;
}
template <typename DataFacadeT>
std::vector<std::uint32_t> const &SegmentList<DataFacadeT>::GetViaIndices() const
{
return via_indices;
}
template <typename DataFacadeT>
std::vector<SegmentInformation> const &SegmentList<DataFacadeT>::Get() const
{
return segments;
}
template <typename DataFacadeT>
void SegmentList<DataFacadeT>::AppendSegment(const FixedPointCoordinate coordinate,
const PathData &path_point)
{
const auto turn = path_point.turn_instruction;
segments.emplace_back(coordinate, path_point.name_id, path_point.segment_duration, 0.f, turn,
path_point.travel_mode);
}
template <typename DataFacadeT>
void SegmentList<DataFacadeT>::Finalize(const bool extract_alternative,
const InternalRouteResult &raw_route,
const unsigned zoom_level,
const bool allow_simplification)
{
if (segments.empty())
return;
// check if first two segments can be merged
BOOST_ASSERT(segments.size() >= 2);
if (segments[0].location == segments[1].location &&
segments[1].turn_instruction == extractor::TurnInstruction::NoTurn)
{
segments[0].travel_mode = segments[1].travel_mode;
segments[0].name_id = segments[1].name_id;
// Other data??
segments.erase(segments.begin() + 1);
}
// announce mode changes
for (std::size_t i = 0; i + 1 < segments.size(); ++i)
{
auto &segment = segments[i];
const auto next_mode = segments[i + 1].travel_mode;
if (segment.travel_mode != next_mode &&
segment.turn_instruction == extractor::TurnInstruction::NoTurn)
{
segment.turn_instruction = extractor::TurnInstruction::GoStraight;
segment.necessary = true;
}
}
segments[0].length = 0.f;
for (const auto i : util::irange<std::size_t>(1, segments.size()))
{
// move down names by one, q&d hack
segments[i - 1].name_id = segments[i].name_id;
segments[i].length = util::coordinate_calculation::greatCircleDistance(
segments[i - 1].location, segments[i].location);
}
float segment_length = 0.;
EdgeWeight segment_duration = 0;
std::size_t segment_start_index = 0;
double path_length = 0;
for (const auto i : util::irange<std::size_t>(1, segments.size()))
{
path_length += segments[i].length;
segment_length += segments[i].length;
segment_duration += segments[i].duration;
segments[segment_start_index].length = segment_length;
segments[segment_start_index].duration = segment_duration;
if (extractor::TurnInstruction::NoTurn != segments[i].turn_instruction)
{
BOOST_ASSERT(segments[i].necessary);
segment_length = 0;
segment_duration = 0;
segment_start_index = i;
if (segments[i].turn_instruction == extractor::TurnInstruction::NameChanges)
{
segments[i].turn_instruction =
extractor::TurnInstruction::GoStraight; // to not break the api
}
}
}
total_distance = static_cast<std::uint32_t>(std::round(path_length));
total_duration = static_cast<std::uint32_t>(std::round(
(extract_alternative ? raw_route.alternative_path_length : raw_route.shortest_path_length) /
10.));
// Post-processing to remove empty or nearly empty path segments
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.back().length &&
!(segments.end() - 2)->is_via_location)
{
segments.pop_back();
segments.back().necessary = true;
segments.back().turn_instruction = extractor::TurnInstruction::NoTurn;
}
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.front().length &&
!(segments.begin() + 1)->is_via_location)
{
segments.erase(segments.begin());
segments.front().turn_instruction = extractor::TurnInstruction::HeadOn;
segments.front().necessary = true;
}
if (allow_simplification)
{
douglasPeucker(segments, zoom_level);
}
std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces
via_indices.push_back(0);
const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first,
const SegmentInformation &second)
{
if (!first.necessary)
return;
// mark the end of a leg (of several segments)
if (first.is_via_location)
via_indices.push_back(necessary_segments);
const double post_turn_bearing =
util::coordinate_calculation::bearing(first.location, second.location);
const double pre_turn_bearing =
util::coordinate_calculation::bearing(second.location, first.location);
first.post_turn_bearing = static_cast<short>(post_turn_bearing * 10);
first.pre_turn_bearing = static_cast<short>(pre_turn_bearing * 10);
++necessary_segments;
};
// calculate which segments are necessary and update segments for bearings
util::for_each_pair(segments, markNecessarySegments);
via_indices.push_back(necessary_segments);
BOOST_ASSERT(via_indices.size() >= 2);
}
template <typename DataFacadeT>
SegmentList<DataFacadeT> MakeSegmentList(const InternalRouteResult &raw_route,
const bool extract_alternative,
const unsigned zoom_level,
const bool allow_simplification,
const DataFacadeT *facade)
{
return SegmentList<DataFacadeT>(raw_route, extract_alternative, zoom_level,
allow_simplification, facade);
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_

View File

@ -0,0 +1,26 @@
#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP
#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP
#include "util/coordinate.hpp"
#include "extractor/turn_instructions.hpp"
namespace osrm
{
namespace engine
{
namespace guidance
{
struct StepManeuver
{
util::FixedPointCoordinate location;
double heading_before;
double heading_after;
extractor::TurnInstruction instruction;
};
}
}
}
#endif

View File

@ -1,147 +0,0 @@
#ifndef ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
#define ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
#include "engine/segment_information.hpp"
#include "engine/guidance/segment_list.hpp"
#include "extractor/turn_instructions.hpp"
#include "osrm/json_container.hpp"
#include "util/bearing.hpp"
#include "util/cast.hpp"
#include <cstdint>
#include <limits>
#include <string>
#include <utility>
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
template <typename DataFacadeT>
inline util::json::Array AnnotateRoute(const std::vector<SegmentInformation> &route_segments,
DataFacadeT *facade)
{
util::json::Array json_instruction_array;
if (route_segments.empty())
return json_instruction_array;
// Segment information has following format:
//["instruction id","streetname",length,position,time,"length","earth_direction",azimuth]
std::int32_t necessary_segments_running_index = 0;
struct RoundAbout
{
std::int32_t start_index;
std::uint32_t name_id;
std::int32_t leave_at_exit;
} round_about;
round_about = {std::numeric_limits<std::int32_t>::max(), 0, 0};
std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction;
extractor::TravelMode last_travel_mode = TRAVEL_MODE_DEFAULT;
// Generate annotations for every segment
for (std::size_t i = 0; i < route_segments.size(); ++i)
{
const auto &segment = route_segments[i];
util::json::Array json_instruction_row;
extractor::TurnInstruction current_instruction = segment.turn_instruction;
if (extractor::isTurnNecessary(current_instruction))
{
if (extractor::TurnInstruction::EnterRoundAbout == current_instruction)
{
round_about.name_id = segment.name_id;
round_about.start_index = necessary_segments_running_index;
}
else
{
std::string current_turn_instruction;
if (extractor::TurnInstruction::LeaveRoundAbout == current_instruction)
{
temp_instruction = std::to_string(util::cast::enum_to_underlying(
extractor::TurnInstruction::EnterRoundAbout));
current_turn_instruction += temp_instruction;
current_turn_instruction += "-";
temp_instruction = std::to_string(round_about.leave_at_exit + 1);
current_turn_instruction += temp_instruction;
round_about.leave_at_exit = 0;
}
else
{
temp_instruction =
std::to_string(util::cast::enum_to_underlying(current_instruction));
current_turn_instruction += temp_instruction;
}
json_instruction_row.values.emplace_back(std::move(current_turn_instruction));
json_instruction_row.values.push_back(facade->get_name_for_id(segment.name_id));
json_instruction_row.values.push_back(std::round(segment.length));
json_instruction_row.values.push_back(necessary_segments_running_index);
json_instruction_row.values.push_back(std::round(segment.duration / 10.));
json_instruction_row.values.push_back(
std::to_string(static_cast<std::uint32_t>(segment.length)) + "m");
// post turn bearing
const double post_turn_bearing_value = (segment.post_turn_bearing / 10.);
json_instruction_row.values.push_back(util::bearing::get(post_turn_bearing_value));
json_instruction_row.values.push_back(
static_cast<std::uint32_t>(std::round(post_turn_bearing_value)));
if (i + 1 < route_segments.size())
{
// anounce next travel mode with turn
json_instruction_row.values.push_back(route_segments[i + 1].travel_mode);
last_travel_mode = segment.travel_mode;
}
else
{
json_instruction_row.values.push_back(segment.travel_mode);
last_travel_mode = segment.travel_mode;
}
// pre turn bearing
const double pre_turn_bearing_value = (segment.pre_turn_bearing / 10.);
json_instruction_row.values.push_back(util::bearing::get(pre_turn_bearing_value));
json_instruction_row.values.push_back(
static_cast<std::uint32_t>(std::round(pre_turn_bearing_value)));
json_instruction_array.values.push_back(json_instruction_row);
}
}
else if (extractor::TurnInstruction::StayOnRoundAbout == current_instruction)
{
++round_about.leave_at_exit;
}
if (segment.necessary)
{
++necessary_segments_running_index;
}
}
util::json::Array json_last_instruction_row;
temp_instruction = std::to_string(
util::cast::enum_to_underlying(extractor::TurnInstruction::ReachedYourDestination));
json_last_instruction_row.values.emplace_back(std::move(temp_instruction));
json_last_instruction_row.values.push_back("");
json_last_instruction_row.values.push_back(0);
json_last_instruction_row.values.push_back(necessary_segments_running_index - 1);
json_last_instruction_row.values.push_back(0);
json_last_instruction_row.values.push_back("0m");
json_last_instruction_row.values.push_back(util::bearing::get(0.0));
json_last_instruction_row.values.push_back(0.);
json_last_instruction_row.values.push_back(last_travel_mode);
json_last_instruction_row.values.push_back(util::bearing::get(0.0));
json_last_instruction_row.values.push_back(0.);
json_instruction_array.values.emplace_back(std::move(json_last_instruction_row));
return json_instruction_array;
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif

61
include/engine/hint.hpp Normal file
View File

@ -0,0 +1,61 @@
#ifndef ENGINE_HINT_HPP
#define ENGINE_HINT_HPP
#include "engine/object_encoder.hpp"
#include "engine/phantom_node.hpp"
#include <boost/assert.hpp>
#include <cmath>
namespace osrm
{
namespace engine
{
// Is returned as a temporary identifier for snapped coodinates
struct Hint
{
FixedPointCoordinate input_coordinate;
PhantomNode phantom;
std::uint32_t data_checksum;
template <typename DataFacadeT>
bool IsValid(const FixedPointCoordinate new_input_coordinates, DataFacadeT &facade) const
{
auto is_same_input_coordinate = new_input_coordinates.lat == input_coordinate.lat &&
new_input_coordinates.lon == input_coordinate.lon;
return is_same_input_coordinate && phantom.IsValid(facade.GetNumberOfNodes()) &&
facade.GetCheckSum() == data_checksum;
}
std::string ToBase64() const
{
std::string encoded = encodeBase64(*this);
return encoded;
}
static Hint FromBase64(const std::string &base64Hint)
{
BOOST_ASSERT_MSG(base64Hint.size() ==
static_cast<std::size_t>(std::ceil(sizeof(Hint) / 3.) * 4),
"Hint has invalid size");
auto decoded = decodeBase64<Hint>(base64Hint);
return decoded;
}
};
#ifndef _MSC_VER
constexpr std::size_t ENCODED_HINT_SIZE = 88;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
#else
// PhantomNode is bigger under windows because MSVC does not support bit packing
constexpr std::size_t ENCODED_HINT_SIZE = 84;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
#endif
}
}
#endif

View File

@ -17,26 +17,15 @@ namespace engine
struct PathData struct PathData
{ {
PathData() // id of via node of the turn
: node(SPECIAL_NODEID), name_id(INVALID_EDGE_WEIGHT), segment_duration(INVALID_EDGE_WEIGHT), NodeID turn_via_node;
turn_instruction(extractor::TurnInstruction::NoTurn), // name of the street that leads to the turn
travel_mode(TRAVEL_MODE_INACCESSIBLE)
{
}
PathData(NodeID node,
unsigned name_id,
extractor::TurnInstruction turn_instruction,
EdgeWeight segment_duration,
extractor::TravelMode travel_mode)
: node(node), name_id(name_id), segment_duration(segment_duration),
turn_instruction(turn_instruction), travel_mode(travel_mode)
{
}
NodeID node;
unsigned name_id; unsigned name_id;
EdgeWeight segment_duration; // duration that is traveled on the segment until the turn is reached
EdgeWeight duration_until_turn;
// instruction to execute at the turn
extractor::TurnInstruction turn_instruction; extractor::TurnInstruction turn_instruction;
// travel mode of the street that leads to the turn
extractor::TravelMode travel_mode : 4; extractor::TravelMode travel_mode : 4;
}; };

View File

@ -94,7 +94,7 @@ struct PhantomNode
bool operator==(const PhantomNode &other) const { return location == other.location; } bool operator==(const PhantomNode &other) const { return location == other.location; }
template <class OtherT> template <class OtherT>
PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point) explicit PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point)
{ {
forward_node_id = other.forward_edge_based_node_id; forward_node_id = other.forward_edge_based_node_id;
reverse_node_id = other.reverse_edge_based_node_id; reverse_node_id = other.reverse_edge_based_node_id;

View File

@ -1,11 +1,14 @@
#ifndef BASE_PLUGIN_HPP #ifndef BASE_PLUGIN_HPP
#define BASE_PLUGIN_HPP #define BASE_PLUGIN_HPP
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/api/base_parameters.hpp"
#include "engine/phantom_node.hpp" #include "engine/phantom_node.hpp"
#include "engine/status.hpp"
#include "osrm/coordinate.hpp" #include "util/coordinate.hpp"
#include "osrm/json_container.hpp" #include "util/json_container.hpp"
#include "osrm/route_parameters.hpp" #include "util/integer_range.hpp"
#include <algorithm> #include <algorithm>
#include <string> #include <string>
@ -20,38 +23,32 @@ namespace plugins
class BasePlugin class BasePlugin
{ {
public: protected:
enum class Status : int datafacade::BaseDataFacade &facade;
{ BasePlugin(datafacade::BaseDataFacade &facade_) : facade(facade_) {}
Ok = 200,
EmptyResult = 207,
NoSegment = 208,
Error = 400
};
BasePlugin() {} bool CheckAllCoordinates(const std::vector<util::FixedPointCoordinate> &coordinates)
// Maybe someone can explain the pure virtual destructor thing to me (dennis)
virtual ~BasePlugin() {}
virtual const std::string GetDescriptor() const = 0;
virtual Status HandleRequest(const RouteParameters &, util::json::Object &) = 0;
virtual bool check_all_coordinates(const std::vector<util::FixedPointCoordinate> &coordinates,
const unsigned min = 2) const final
{ {
if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates), return !std::any_of(std::begin(coordinates), std::end(coordinates),
[](const util::FixedPointCoordinate coordinate) [](const util::FixedPointCoordinate &coordinate)
{ {
return !coordinate.IsValid(); return !coordinate.IsValid();
})) });
{ }
return false;
} Status Error(const std::string &code,
return true; const std::string &message,
util::json::Object &json_result) const
{
json_result.values["code"] = code;
json_result.values["message"] = message;
return Status::Error;
} }
// Decides whether to use the phantom node from a big or small component if both are found. // Decides whether to use the phantom node from a big or small component if both are found.
// Returns true if all phantom nodes are in the same component after snapping. // Returns true if all phantom nodes are in the same component after snapping.
std::vector<PhantomNode> snapPhantomNodes( std::vector<PhantomNode>
const std::vector<std::pair<PhantomNode, PhantomNode>> &phantom_node_pair_list) const SnapPhantomNodes(const std::vector<PhantomNodePair> &phantom_node_pair_list) const
{ {
const auto check_component_id_is_tiny = const auto check_component_id_is_tiny =
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair) [](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
@ -111,6 +108,69 @@ class BasePlugin
return snapped_phantoms; return snapped_phantoms;
} }
std::vector<PhantomNodePair> GetPhantomNodes(const api::BaseParameters &parameters)
{
std::vector<PhantomNodePair> phantom_node_pairs(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<std::size_t>(0, parameters.coordinates.size()))
{
if (use_hints && parameters.hints[i] &&
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
{
phantom_node_pairs[i].first = parameters.hints[i]->phantom;
// we don't set the second one - it will be marked as invalid
continue;
}
if (use_bearings && parameters.bearings[i])
{
if (use_radiuses && parameters.radiuses[i])
{
phantom_node_pairs[i] =
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
parameters.coordinates[i], *parameters.radiuses[i],
parameters.bearings[i]->bearing, parameters.bearings[i]->range);
}
else
phantom_node_pairs[i] =
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
parameters.coordinates[i], parameters.bearings[i]->bearing,
parameters.bearings[i]->range);
{
}
}
else
{
if (use_radiuses && parameters.radiuses[i])
{
phantom_node_pairs[i] =
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
parameters.coordinates[i], *parameters.radiuses[i]);
}
else
phantom_node_pairs[i] =
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
parameters.coordinates[i]);
{
}
}
// we didn't found a fitting node, return error
if (!phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes()))
{
break;
}
BOOST_ASSERT(phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes()));
BOOST_ASSERT(phantom_node_pairs[i].second.IsValid(facade.GetNumberOfNodes()));
}
return phantom_node_pairs;
}
}; };
} }
} }

View File

@ -1,17 +1,16 @@
#ifndef VIA_ROUTE_HPP #ifndef VIA_ROUTE_HPP
#define VIA_ROUTE_HPP #define VIA_ROUTE_HPP
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/plugins/plugin_base.hpp" #include "engine/plugins/plugin_base.hpp"
#include "engine/api/route_api.hpp"
#include "engine/api_response_generator.hpp"
#include "engine/object_encoder.hpp" #include "engine/object_encoder.hpp"
#include "engine/search_engine.hpp" #include "engine/search_engine_data.hpp"
#include "util/for_each_pair.hpp" #include "engine/routing_algorithms/shortest_path.hpp"
#include "util/integer_range.hpp" #include "engine/routing_algorithms/alternative_path.hpp"
#include "util/make_unique.hpp" #include "engine/routing_algorithms/direct_shortest_path.hpp"
#include "util/simple_logger.hpp" #include "util/json_container.hpp"
#include "util/timing_util.hpp"
#include "osrm/json_container.hpp"
#include <cstdlib> #include <cstdlib>
@ -27,146 +26,20 @@ namespace engine
namespace plugins namespace plugins
{ {
template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin class ViaRoutePlugin final : public BasePlugin
{ {
private: private:
std::string descriptor_string; SearchEngineData heaps;
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr; routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path;
DataFacadeT *facade; routing_algorithms::AlternativeRouting<datafacade::BaseDataFacade> alternative_path;
routing_algorithms::DirectShortestPathRouting<datafacade::BaseDataFacade> direct_shortest_path;
int max_locations_viaroute; int max_locations_viaroute;
public: public:
explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute) explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute);
: descriptor_string("viaroute"), facade(facade),
max_locations_viaroute(max_locations_viaroute)
{
search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade);
}
virtual ~ViaRoutePlugin() {} Status HandleRequest(const api::RouteParameters &route_parameters,
util::json::Object &json_result);
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
util::json::Object &json_result) override final
{
if (max_locations_viaroute > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
{
json_result.values["status_message"] =
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")";
return Status::Error;
}
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Invalid coordinates";
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinate";
return Status::Error;
}
std::vector<PhantomNodePair> phantom_node_pair_list(route_parameters.coordinates.size());
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
for (const auto i : util::irange<std::size_t>(0, route_parameters.coordinates.size()))
{
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
phantom_node_pair_list[i].first =
decodeBase64<PhantomNode>(route_parameters.hints[i]);
if (phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
{
continue;
}
}
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
const int range = input_bearings.size() > 0
? (input_bearings[i].second ? *input_bearings[i].second : 10)
: 180;
phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent(
route_parameters.coordinates[i], bearing, range);
// we didn't found a fitting node, return error
if (!phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
{
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") +
std::to_string(i);
return Status::NoSegment;
}
BOOST_ASSERT(phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()));
BOOST_ASSERT(phantom_node_pair_list[i].second.IsValid(facade->GetNumberOfNodes()));
}
auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list);
InternalRouteResult raw_route;
auto build_phantom_pairs =
[&raw_route](const PhantomNode &first_node, const PhantomNode &second_node)
{
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
};
util::for_each_pair(snapped_phantoms, build_phantom_pairs);
if (1 == raw_route.segment_end_coordinates.size())
{
if (route_parameters.alternate_route && facade->GetCoreSize() == 0)
{
search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(),
raw_route);
}
else
{
search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
}
else
{
search_engine_ptr->shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
// we can only know this after the fact, different SCC ids still
// allow for connection in one direction.
if (raw_route.is_valid())
{
auto generator = MakeApiResponseGenerator(facade);
generator.DescribeRoute(route_parameters, raw_route, json_result);
json_result.values["status_message"] = "Found route between points";
}
else
{
auto first_component_id = snapped_phantoms.front().component.id;
auto not_in_same_component =
std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
[first_component_id](const PhantomNode &node)
{
return node.component.id != first_component_id;
});
if (not_in_same_component)
{
json_result.values["status_message"] = "Impossible route between points";
return Status::EmptyResult;
}
else
{
json_result.values["status_message"] = "No route found between points";
return Status::Error;
}
}
return Status::Ok;
}
}; };
} }
} }

View File

@ -2,7 +2,6 @@
#define POLYLINECOMPRESSOR_H_ #define POLYLINECOMPRESSOR_H_
#include "osrm/coordinate.hpp" #include "osrm/coordinate.hpp"
#include "engine/segment_information.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
@ -11,13 +10,21 @@ namespace osrm
{ {
namespace engine namespace engine
{ {
namespace detail
{
constexpr double POLYLINE_PECISION = 1e5;
constexpr double COORDINATE_TO_POLYLINE = POLYLINE_PECISION / COORDINATE_PRECISION;
constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_PECISION;
}
using CoordVectorForwardIter = std::vector<FixedPointCoordinate>::const_iterator;
// Encodes geometry into polyline format. // Encodes geometry into polyline format.
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm // See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
std::string polylineEncode(const std::vector<SegmentInformation> &geometry); std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end);
// Decodes geometry from polyline format // Decodes geometry from polyline format
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm // See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
std::vector<util::FixedPointCoordinate> polylineDecode(const std::string &polyline); std::vector<util::FixedPointCoordinate> decodePolyline(const std::string &polyline);
} }
} }

View File

@ -1,24 +0,0 @@
#ifndef POLYLINE_FORMATTER_HPP
#define POLYLINE_FORMATTER_HPP
#include "engine/segment_information.hpp"
#include "osrm/json_container.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace engine
{
// Encodes geometry into polyline format, returning an encoded JSON object
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
util::json::String polylineEncodeAsJSON(const std::vector<SegmentInformation> &geometry);
// Does not encode the geometry in polyline format, instead returning an unencoded JSON object
util::json::Array polylineUnencodedAsJSON(const std::vector<SegmentInformation> &geometry);
}
}
#endif /* POLYLINE_FORMATTER_HPP */

View File

@ -1,150 +0,0 @@
#ifndef EXTRACT_ROUTE_NAMES_H
#define EXTRACT_ROUTE_NAMES_H
#include <boost/assert.hpp>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
namespace osrm
{
namespace engine
{
struct RouteNames
{
std::string shortest_path_name_1;
std::string shortest_path_name_2;
std::string alternative_path_name_1;
std::string alternative_path_name_2;
};
namespace detail
{
template <class SegmentT>
SegmentT pickNextLongestSegment(const std::vector<SegmentT> &segment_list,
const unsigned blocked_name_id)
{
SegmentT result_segment;
result_segment.name_id = blocked_name_id; // make sure we get a valid name
result_segment.length = 0;
for (const SegmentT &segment : segment_list)
{
if (segment.name_id != blocked_name_id && segment.length > result_segment.length &&
segment.name_id != 0)
{
result_segment = segment;
}
}
return result_segment;
}
} // ns detail
template <class DataFacadeT, class SegmentT>
RouteNames extractRouteNames(std::vector<SegmentT> &shortest_path_segments,
std::vector<SegmentT> &alternative_path_segments,
const DataFacadeT *facade)
{
RouteNames route_names;
if (shortest_path_segments.empty())
{
return route_names;
}
SegmentT shortest_segment_1, shortest_segment_2;
SegmentT alternative_segment_1, alternative_segment_2;
const auto length_comperator = [](const SegmentT &a, const SegmentT &b)
{
return a.length > b.length;
};
const auto name_id_comperator = [](const SegmentT &a, const SegmentT &b)
{
return a.name_id < b.name_id;
};
// pick the longest segment for the shortest path.
std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator);
shortest_segment_1 = shortest_path_segments[0];
if (!alternative_path_segments.empty())
{
std::sort(alternative_path_segments.begin(), alternative_path_segments.end(),
length_comperator);
// also pick the longest segment for the alternative path
alternative_segment_1 = alternative_path_segments[0];
}
// compute the set difference (for shortest path) depending on names between shortest and
// alternative
std::vector<SegmentT> shortest_path_set_difference(shortest_path_segments.size());
std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), name_id_comperator);
std::sort(alternative_path_segments.begin(), alternative_path_segments.end(),
name_id_comperator);
std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(),
alternative_path_segments.begin(), alternative_path_segments.end(),
shortest_path_set_difference.begin(), name_id_comperator);
std::sort(shortest_path_set_difference.begin(), shortest_path_set_difference.end(),
length_comperator);
shortest_segment_2 =
pickNextLongestSegment(shortest_path_set_difference, shortest_segment_1.name_id);
// compute the set difference (for alternative path) depending on names between shortest and
// alternative
// vectors are still sorted, no need to do again
BOOST_ASSERT(std::is_sorted(shortest_path_segments.begin(), shortest_path_segments.end(),
name_id_comperator));
BOOST_ASSERT(std::is_sorted(alternative_path_segments.begin(), alternative_path_segments.end(),
name_id_comperator));
std::vector<SegmentT> alternative_path_set_difference(alternative_path_segments.size());
std::set_difference(alternative_path_segments.begin(), alternative_path_segments.end(),
shortest_path_segments.begin(), shortest_path_segments.end(),
alternative_path_set_difference.begin(), name_id_comperator);
std::sort(alternative_path_set_difference.begin(), alternative_path_set_difference.end(),
length_comperator);
if (!alternative_path_segments.empty())
{
alternative_segment_2 =
pickNextLongestSegment(alternative_path_set_difference, alternative_segment_1.name_id);
}
// move the segments into the order in which they occur.
if (shortest_segment_1.position > shortest_segment_2.position)
{
std::swap(shortest_segment_1, shortest_segment_2);
}
// fetching names for the selected segments
route_names.shortest_path_name_1 = facade->get_name_for_id(shortest_segment_1.name_id);
route_names.shortest_path_name_2 = facade->get_name_for_id(shortest_segment_2.name_id);
if (!alternative_path_segments.empty())
{
if (alternative_segment_1.position > alternative_segment_2.position)
{
std::swap(alternative_segment_1, alternative_segment_2);
}
route_names.alternative_path_name_1 =
facade->get_name_for_id(alternative_segment_1.name_id);
route_names.alternative_path_name_2 =
facade->get_name_for_id(alternative_segment_2.name_id);
}
return route_names;
}
}
}
#endif // EXTRACT_ROUTE_NAMES_H

View File

@ -1,129 +0,0 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ROUTE_PARAMETERS_HPP
#define ROUTE_PARAMETERS_HPP
#include "osrm/coordinate.hpp"
#include <boost/optional/optional.hpp>
#include <string>
#include <vector>
namespace osrm
{
namespace engine
{
struct RouteParameters
{
RouteParameters();
void SetZoomLevel(const short level);
void SetNumberOfResults(const short number);
void SetAlternateRouteFlag(const bool flag);
void SetUTurn(const bool flag);
void SetAllUTurns(const bool flag);
void SetClassify(const bool classify);
void SetMatchingBeta(const double beta);
void SetGPSPrecision(const double precision);
void SetDeprecatedAPIFlag(const std::string &);
void SetChecksum(const unsigned check_sum);
void SetInstructionFlag(const bool flag);
void SetService(const std::string &service);
void SetOutputFormat(const std::string &format);
void SetJSONpParameter(const std::string &parameter);
void AddHint(const std::string &hint);
void AddTimestamp(const unsigned timestamp);
bool AddBearing(int bearing, boost::optional<int> range);
void SetLanguage(const std::string &language);
void SetGeometryFlag(const bool flag);
void SetCompressionFlag(const bool flag);
void AddCoordinate(const double latitude, const double longitude);
void AddDestination(const double latitude, const double longitude);
void AddSource(const double latitude, const double longitude);
void SetCoordinatesFromGeometry(const std::string &geometry_string);
bool SetX(const int x);
bool SetZ(const int z);
bool SetY(const int y);
short zoom_level;
bool print_instructions;
bool alternate_route;
bool geometry;
bool compression;
bool deprecatedAPI;
bool uturn_default;
bool classify;
double matching_beta;
double gps_precision;
unsigned check_sum;
short num_results;
std::string service;
std::string output_format;
std::string jsonp_parameter;
std::string language;
std::vector<std::string> hints;
std::vector<unsigned> timestamps;
std::vector<std::pair<const int, const boost::optional<int>>> bearings;
std::vector<bool> uturns;
std::vector<FixedPointCoordinate> coordinates;
std::vector<bool> is_destination;
std::vector<bool> is_source;
int z;
int x;
int y;
};
}
}
#endif // ROUTE_PARAMETERS_HPP

View File

@ -40,11 +40,8 @@ class DirectShortestPathRouting final
~DirectShortestPathRouting() {} ~DirectShortestPathRouting() {}
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector, void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
const std::vector<bool> &uturn_indicators,
InternalRouteResult &raw_route_data) const InternalRouteResult &raw_route_data) const
{ {
(void)uturn_indicators; // unused
// Get distance to next pair of target nodes. // Get distance to next pair of target nodes.
BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(), BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(),
"Direct Shortest Path Query only accepts a single source and target pair. " "Direct Shortest Path Query only accepts a single source and target pair. "

View File

@ -24,13 +24,6 @@ namespace osrm
namespace engine namespace engine
{ {
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3;
namespace routing_algorithms namespace routing_algorithms
{ {
@ -327,13 +320,13 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
BOOST_ASSERT(start_index < end_index); BOOST_ASSERT(start_index < end_index);
for (std::size_t i = start_index; i < end_index; ++i) for (std::size_t i = start_index; i < end_index; ++i)
{ {
unpacked_path.emplace_back(id_vector[i], name_index, unpacked_path.push_back(PathData{id_vector[i], name_index, weight_vector[i],
extractor::TurnInstruction::NoTurn, weight_vector[i], extractor::TurnInstruction::NoTurn,
travel_mode); travel_mode});
} }
BOOST_ASSERT(unpacked_path.size() > 0); BOOST_ASSERT(unpacked_path.size() > 0);
unpacked_path.back().turn_instruction = turn_instruction; unpacked_path.back().turn_instruction = turn_instruction;
unpacked_path.back().segment_duration += (ed.distance - total_weight); unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
} }
} }
std::vector<unsigned> id_vector; std::vector<unsigned> id_vector;
@ -373,7 +366,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0); BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
unpacked_path.emplace_back( unpacked_path.emplace_back(
PathData{id_vector[i], phantom_node_pair.target_phantom.name_id, PathData{id_vector[i], phantom_node_pair.target_phantom.name_id,
extractor::TurnInstruction::NoTurn, 0, 0, extractor::TurnInstruction::NoTurn,
target_traversed_in_reverse target_traversed_in_reverse
? phantom_node_pair.target_phantom.backward_travel_mode ? phantom_node_pair.target_phantom.backward_travel_mode
: phantom_node_pair.target_phantom.forward_travel_mode}); : phantom_node_pair.target_phantom.forward_travel_mode});
@ -392,7 +385,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
// looks like a trivially true check but tests for underflow // looks like a trivially true check but tests for underflow
BOOST_ASSERT(last_index > second_to_last_index); BOOST_ASSERT(last_index > second_to_last_index);
if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node) if (unpacked_path[last_index].turn_via_node == unpacked_path[second_to_last_index].turn_via_node)
{ {
unpacked_path.pop_back(); unpacked_path.pop_back();
} }
@ -755,7 +748,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
double distance = 0; double distance = 0;
for (const auto &p : unpacked_path) for (const auto &p : unpacked_path)
{ {
current_coordinate = facade->GetCoordinateOfNode(p.node); current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node);
distance += util::coordinate_calculation::haversineDistance(previous_coordinate, distance += util::coordinate_calculation::haversineDistance(previous_coordinate,
current_coordinate); current_coordinate);
previous_coordinate = current_coordinate; previous_coordinate = current_coordinate;

View File

@ -9,6 +9,7 @@
#include "util/integer_range.hpp" #include "util/integer_range.hpp"
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/optional.hpp>
namespace osrm namespace osrm
{ {
@ -236,11 +237,13 @@ class ShortestPathRouting final
} }
} }
static const constexpr bool UTURN_DEFAULT = false;
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector, void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
const std::vector<bool> &uturn_indicators, const std::vector<boost::optional<bool>> &uturn_indicators,
InternalRouteResult &raw_route_data) const InternalRouteResult &raw_route_data) const
{ {
BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1); BOOST_ASSERT(uturn_indicators.empty() || uturn_indicators.size() == phantom_nodes_vector.size() + 1);
engine_working_data.InitializeOrClearFirstThreadLocalStorage( engine_working_data.InitializeOrClearFirstThreadLocalStorage(
super::facade->GetNumberOfNodes()); super::facade->GetNumberOfNodes());
engine_working_data.InitializeOrClearSecondThreadLocalStorage( engine_working_data.InitializeOrClearSecondThreadLocalStorage(
@ -266,6 +269,8 @@ class ShortestPathRouting final
std::vector<NodeID> total_packed_path_to_reverse; std::vector<NodeID> total_packed_path_to_reverse;
std::vector<std::size_t> packed_leg_to_reverse_begin; std::vector<std::size_t> packed_leg_to_reverse_begin;
const bool use_uturn_indicators = !uturn_indicators.empty();
std::size_t current_leg = 0; std::size_t current_leg = 0;
// this implements a dynamic program that finds the shortest route through // this implements a dynamic program that finds the shortest route through
// a list of vias // a list of vias
@ -280,8 +285,8 @@ class ShortestPathRouting final
const auto &source_phantom = phantom_node_pair.source_phantom; const auto &source_phantom = phantom_node_pair.source_phantom;
const auto &target_phantom = phantom_node_pair.target_phantom; const auto &target_phantom = phantom_node_pair.target_phantom;
BOOST_ASSERT(current_leg + 1 < uturn_indicators.size()); const bool use_uturn_default = !use_uturn_indicators || !uturn_indicators[current_leg + 1];
const bool allow_u_turn_at_via = uturn_indicators[current_leg + 1]; const bool allow_u_turn_at_via = (use_uturn_default && UTURN_DEFAULT) || (!use_uturn_default && *uturn_indicators[current_leg + 1]);
bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID; bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID;
bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID; bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID;

View File

@ -1,47 +0,0 @@
#ifndef SEARCH_ENGINE_HPP
#define SEARCH_ENGINE_HPP
#include "engine/search_engine_data.hpp"
#include "engine/routing_algorithms/alternative_path.hpp"
#include "engine/routing_algorithms/many_to_many.hpp"
#include "engine/routing_algorithms/map_matching.hpp"
#include "engine/routing_algorithms/shortest_path.hpp"
#include "engine/routing_algorithms/direct_shortest_path.hpp"
#include <type_traits>
namespace osrm
{
namespace engine
{
template <class DataFacadeT> class SearchEngine
{
private:
DataFacadeT *facade;
SearchEngineData engine_working_data;
public:
routing_algorithms::ShortestPathRouting<DataFacadeT> shortest_path;
routing_algorithms::DirectShortestPathRouting<DataFacadeT> direct_shortest_path;
routing_algorithms::AlternativeRouting<DataFacadeT> alternative_path;
routing_algorithms::ManyToManyRouting<DataFacadeT> distance_table;
routing_algorithms::MapMatching<DataFacadeT> map_matching;
explicit SearchEngine(DataFacadeT *facade)
: facade(facade), shortest_path(facade, engine_working_data),
direct_shortest_path(facade, engine_working_data),
alternative_path(facade, engine_working_data),
distance_table(facade, engine_working_data), map_matching(facade, engine_working_data)
{
static_assert(!std::is_pointer<DataFacadeT>::value, "don't instantiate with ptr type");
static_assert(std::is_object<DataFacadeT>::value,
"don't instantiate with void, function, or reference");
}
~SearchEngine() {}
};
}
}
#endif // SEARCH_ENGINE_HPP

View File

@ -1,60 +0,0 @@
#ifndef SEGMENT_INFORMATION_HPP
#define SEGMENT_INFORMATION_HPP
#include "extractor/turn_instructions.hpp"
#include "extractor/travel_mode.hpp"
#include "util/typedefs.hpp"
#include "osrm/coordinate.hpp"
#include <utility>
namespace osrm
{
namespace engine
{
// Struct fits everything in one cache line
struct SegmentInformation
{
util::FixedPointCoordinate location;
NodeID name_id;
EdgeWeight duration;
float length;
short pre_turn_bearing; // more than enough [0..3600] fits into 12 bits
short post_turn_bearing;
extractor::TurnInstruction turn_instruction;
extractor::TravelMode travel_mode;
bool necessary;
bool is_via_location;
explicit SegmentInformation(util::FixedPointCoordinate location,
const NodeID name_id,
const EdgeWeight duration,
const float length,
const extractor::TurnInstruction turn_instruction,
const bool necessary,
const bool is_via_location,
const extractor::TravelMode travel_mode)
: location(std::move(location)), name_id(name_id), duration(duration), length(length),
pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction),
travel_mode(travel_mode), necessary(necessary), is_via_location(is_via_location)
{
}
explicit SegmentInformation(util::FixedPointCoordinate location,
const NodeID name_id,
const EdgeWeight duration,
const float length,
const extractor::TurnInstruction turn_instruction,
const extractor::TravelMode travel_mode)
: location(std::move(location)), name_id(name_id), duration(duration), length(length),
pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction),
travel_mode(travel_mode),
necessary(turn_instruction != extractor::TurnInstruction::NoTurn), is_via_location(false)
{
}
};
}
}
#endif /* SEGMENT_INFORMATION_HPP */

18
include/engine/status.hpp Normal file
View File

@ -0,0 +1,18 @@
#ifndef ENGINE_STATUS_HPP
#define ENGINE_STATUS_HPP
namespace osrm
{
namespace engine
{
enum class Status
{
Ok,
Error
};
}
}
#endif

View File

@ -28,13 +28,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef OSRM_HPP #ifndef OSRM_HPP
#define OSRM_HPP #define OSRM_HPP
#include "osrm/status.hpp"
#include <memory> #include <memory>
namespace osrm namespace osrm
{ {
struct LibOSRMConfig;
namespace util namespace util
{ {
namespace json namespace json
@ -47,11 +46,14 @@ namespace engine
{ {
class Engine; class Engine;
struct EngineConfig; struct EngineConfig;
namespace api
{
struct RouteParameters; struct RouteParameters;
} }
}
using engine::EngineConfig; using engine::EngineConfig;
using engine::RouteParameters; using engine::api::RouteParameters;
namespace json = util::json; namespace json = util::json;
class OSRM class OSRM
@ -61,8 +63,8 @@ class OSRM
public: public:
OSRM(EngineConfig &lib_config); OSRM(EngineConfig &lib_config);
~OSRM(); // needed because we need to define it with the implementation of OSRM_impl ~OSRM(); // needed for unique_ptr + impl abstraction
int RunQuery(const RouteParameters &route_parameters, json::Object &json_result); Status Route(const RouteParameters &route_parameters, json::Object &json_result);
}; };
} }

View File

@ -1,11 +1,11 @@
#ifndef GLOBAL_ROUTE_PARAMETERS_HPP #ifndef GLOBAL_ROUTE_PARAMETERS_HPP
#define GLOBAL_ROUTE_PARAMETERS_HPP #define GLOBAL_ROUTE_PARAMETERS_HPP
#include "engine/route_parameters.hpp" #include "engine/api/route_parameters.hpp"
namespace osrm namespace osrm
{ {
using engine::RouteParameters; using engine::api::RouteParameters;
} }
#endif #endif

11
include/osrm/status.hpp Normal file
View File

@ -0,0 +1,11 @@
#ifndef OSRM_STATUS_HPP
#define OSRM_STATUS_HPP
#include "engine/status.hpp"
namespace osrm
{
using engine::Status;
}
#endif

View File

@ -0,0 +1,82 @@
#ifndef SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP
#define SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP
#include "engine/api/base_parameters.hpp"
#include "engine/polyline_compressor.hpp"
#include "engine/hint.hpp"
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_int.hpp>
#include <boost/spirit/include/qi_real.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_optional.hpp>
#include <boost/spirit/include/qi_attr_cast.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_repeat.hpp>
#include <boost/spirit/include/qi_as_string.hpp>
#include <string>
namespace osrm
{
namespace server
{
namespace api
{
namespace qi = boost::spirit::qi;
struct BaseParametersGrammar : boost::spirit::qi::grammar<std::string::iterator>
{
using Iterator = std::string::iterator;
using RadiusesT = std::vector<boost::optional<double>>;
BaseParametersGrammar(qi::rule<Iterator> &child_rule,
engine::api::BaseParameters &parameters_)
: BaseParametersGrammar::base_type(child_rule), base_parameters(parameters_)
{
const auto add_bearing = [this](const boost::optional<boost::fusion::vector<short, short>> &bearing_range) {
boost::optional<engine::api::BaseParameters::Bearing> bearing;
if (bearing_range)
{
bearing = engine::api::BaseParameters::Bearing {boost::fusion::at_c<0>(*bearing_range), boost::fusion::at_c<1>(*bearing_range)};
}
base_parameters.bearings.push_back(std::move(bearing));
};
const auto set_radiuses = [this](RadiusesT& radiuses) {
base_parameters.radiuses = std::move(radiuses);
};
const auto add_hint = [this](const std::string& hint_string) {
if (hint_string.size() > 0)
{
base_parameters.hints.push_back(engine::Hint::FromBase64(hint_string));
}
};
alpha_numeral = +qi::char_("a-zA-Z0-9");
base64_char = qi::char_("a-zA-Z0-9--_");
radiuses_rule = qi::lit("radiuses=") >> -qi::double_ % ";";
hints_rule = qi::lit("hints=") >> qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";";
bearings_rule =
qi::lit("bearings=") >> -((qi::short_ >> ',' >> qi::short_))[add_bearing] % ";";
base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule;
}
qi::rule<Iterator> base_rule;
private:
engine::api::BaseParameters &base_parameters;
qi::rule<Iterator> bearings_rule;
qi::rule<Iterator> hints_rule;
qi::rule<Iterator, RadiusesT()> radiuses_rule;
qi::rule<Iterator, unsigned char()> base64_char;
qi::rule<Iterator, std::string()> alpha_numeral;
};
}
}
}
#endif

View File

@ -0,0 +1,29 @@
#ifndef SERVER_API_PARSED_URL_HPP
#define SERVER_API_PARSED_URL_HPP
#include "util/coordinate.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace server
{
namespace api
{
struct ParsedURL
{
std::string service;
unsigned version;
std::string profile;
std::vector<util::FixedPointCoordinate> coordinates;
std::string options;
};
}
}
}
#endif

View File

@ -0,0 +1,92 @@
#ifndef ROUTE_PARAMETERS_GRAMMAR_HPP
#define ROUTE_PARAMETERS_GRAMMAR_HPP
#include "engine/api/route_parameters.hpp"
#include "server/api/base_parameters_grammar.hpp"
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_optional.hpp>
namespace osrm
{
namespace server
{
namespace api
{
namespace qi = boost::spirit::qi;
struct RouteParametersGrammar : public BaseParametersGrammar
{
using Iterator = std::string::iterator;
using StepsT = bool;
using AlternativeT = bool;
using GeometriesT = engine::api::RouteParameters::GeometriesType;
using OverviewT = engine::api::RouteParameters::OverviewType;
using UturnsT = std::vector<boost::optional<bool>>;
RouteParametersGrammar()
: BaseParametersGrammar(root_rule, route_parameters)
{
const auto set_geojson_type = [this]()
{
route_parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON;
};
const auto set_polyline_type = [this]()
{
route_parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline;
};
const auto set_simplified_type = [this]()
{
route_parameters.overview = engine::api::RouteParameters::OverviewType::Simplified;
};
const auto set_full_type = [this]()
{
route_parameters.overview = engine::api::RouteParameters::OverviewType::Full;
};
const auto set_false_type = [this]()
{
route_parameters.overview = engine::api::RouteParameters::OverviewType::False;
};
const auto set_steps = [this](const StepsT steps)
{
route_parameters.steps = steps;
};
const auto set_alternative = [this](const AlternativeT alternative)
{
route_parameters.alternative = alternative;
};
const auto set_uturns = [this](UturnsT &uturns)
{
route_parameters.uturns = std::move(uturns);
};
alternative_rule = qi::lit("alternative=") >> qi::bool_;
steps_rule = qi::lit("steps=") >> qi::bool_;
geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
qi::lit("geometries=polyline")[set_polyline_type];
overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
qi::lit("overview=full")[set_full_type] |
qi::lit("overview=false")[set_false_type];
uturns_rule = qi::lit("uturns=") >> -qi::bool_ % ";";
route_rule = steps_rule[set_steps] | alternative_rule[set_alternative] | geometries_rule |
overview_rule | uturns_rule[set_uturns];
root_rule = -((base_rule | route_rule) % '&');
}
engine::api::RouteParameters route_parameters;
private:
qi::rule<Iterator> root_rule, route_rule, geometries_rule, overview_rule;
qi::rule<Iterator, UturnsT()> uturns_rule;
qi::rule<Iterator, StepsT()> steps_rule;
qi::rule<Iterator, AlternativeT()> alternative_rule;
};
}
}
}
#endif

View File

@ -0,0 +1,28 @@
#ifndef SERVER_API_ROUTE_PARAMETERS_PARSER_HPP
#define SERVER_API_ROUTE_PARAMETERS_PARSER_HPP
#include "engine/api/route_parameters.hpp"
#include "server/api/route_parameters_parser.hpp"
namespace osrm
{
namespace server
{
namespace api
{
// Starts parsing and iter and modifies it until iter == end or parsing failed
boost::optional<engine::api::RouteParameters> parseRouteParameters(std::string::iterator& iter, std::string::iterator end);
// copy on purpose because we need mutability
inline boost::optional<engine::api::RouteParameters> parseRouteParameters(std::string options_string)
{
auto iter = options_string.begin();
return parseRouteParameters(iter, options_string.end());
}
}
}
}
#endif

View File

@ -0,0 +1,30 @@
#ifndef SERVER_URL_PARSER_HPP
#define SERVER_URL_PARSER_HPP
#include "server/api/parsed_url.hpp"
#include <boost/optional.hpp>
#include <string>
namespace osrm
{
namespace server
{
namespace api
{
// Starts parsing and iter and modifies it until iter == end or parsing failed
boost::optional<ParsedURL> parseURL(std::string::iterator& iter, std::string::iterator end);
// copy on purpose because we need mutability
inline boost::optional<ParsedURL> parseURL(std::string url_string)
{
auto iter = url_string.begin();
return parseURL(iter, url_string.end());
}
}
}
}
#endif

View File

@ -1,151 +0,0 @@
#ifndef API_GRAMMAR_HPP
#define API_GRAMMAR_HPP
#include <boost/bind.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_action.hpp>
namespace osrm
{
namespace server
{
namespace qi = boost::spirit::qi;
template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Iterator>
{
explicit APIGrammar(HandlerT *h) : APIGrammar::base_type(api_call), handler(h)
{
const auto set_x_wrapper = [this](const int x, bool &pass)
{
pass = handler->SetX(x);
};
const auto set_y_wrapper = [this](const int y, bool &pass)
{
pass = handler->SetY(y);
};
const auto set_z_wrapper = [this](const int z, bool &pass)
{
pass = handler->SetZ(z);
};
const auto add_bearing_wrapper = [this](
const boost::fusion::vector<int, boost::optional<int>> &received_bearing, bool &pass)
{
const int bearing = boost::fusion::at_c<0>(received_bearing);
const boost::optional<int> range = boost::fusion::at_c<1>(received_bearing);
pass = handler->AddBearing(bearing, range);
};
const auto add_coordinate_wrapper =
[this](const boost::fusion::vector<double, double> &received_coordinate)
{
handler->AddCoordinate(boost::fusion::at_c<0>(received_coordinate),
boost::fusion::at_c<1>(received_coordinate));
};
const auto add_source_wrapper =
[this](const boost::fusion::vector<double, double> &received_coordinate)
{
handler->AddSource(boost::fusion::at_c<0>(received_coordinate),
boost::fusion::at_c<1>(received_coordinate));
};
const auto add_destination_wrapper =
[this](const boost::fusion::vector<double, double> &received_coordinate)
{
handler->AddDestination(boost::fusion::at_c<0>(received_coordinate),
boost::fusion::at_c<1>(received_coordinate));
};
api_call =
qi::lit('/') >> string[boost::bind(&HandlerT::SetService, handler, ::_1)] >> -query;
query = ('?') >> +(zoom | output | jsonp | checksum | uturns | location_with_options |
destination_with_options | source_with_options | cmp | language |
instruction | geometry | alt_route | old_API | num_results |
matching_beta | gps_precision | classify | locs | x | y | z);
// all combinations of timestamp, uturn, hint and bearing without duplicates
t_u = (u >> -timestamp) | (timestamp >> -u);
t_h = (hint >> -timestamp) | (timestamp >> -hint);
u_h = (u >> -hint) | (hint >> -u);
t_u_h = (hint >> -t_u) | (u >> -t_h) | (timestamp >> -u_h);
location_options =
(bearing >> -t_u_h) | (t_u_h >> -bearing) | //
(u >> bearing >> -t_h) | (timestamp >> bearing >> -u_h) | (hint >> bearing >> t_u) | //
(t_h >> bearing >> -u) | (u_h >> bearing >> -timestamp) | (t_u >> bearing >> -hint);
location_with_options = location >> -location_options;
source_with_options = source >> -location_options;
destination_with_options = destination >> -location_options;
zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >>
qi::short_[boost::bind(&HandlerT::SetZoomLevel, handler, ::_1)];
output = (-qi::lit('&')) >> qi::lit("output=json");
jsonp = (-qi::lit('&')) >> qi::lit("jsonp") >> '=' >>
stringwithPercent[boost::bind(&HandlerT::SetJSONpParameter, handler, ::_1)];
checksum = (-qi::lit('&')) >> qi::lit("checksum") >> '=' >>
qi::uint_[boost::bind(&HandlerT::SetChecksum, handler, ::_1)];
instruction = (-qi::lit('&')) >> qi::lit("instructions") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetInstructionFlag, handler, ::_1)];
geometry = (-qi::lit('&')) >> qi::lit("geometry") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetGeometryFlag, handler, ::_1)];
cmp = (-qi::lit('&')) >> qi::lit("compression") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetCompressionFlag, handler, ::_1)];
location = (-qi::lit('&')) >> qi::lit("loc") >> '=' >>
(qi::double_ >> qi::lit(',') >>
qi::double_)[boost::bind<void>(add_coordinate_wrapper, ::_1)];
destination = (-qi::lit('&')) >> qi::lit("dst") >> '=' >>
(qi::double_ >> qi::lit(',') >>
qi::double_)[boost::bind<void>(add_destination_wrapper, ::_1)];
source = (-qi::lit('&')) >> qi::lit("src") >> '=' >>
(qi::double_ >> qi::lit(',') >>
qi::double_)[boost::bind<void>(add_source_wrapper, ::_1)];
hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >>
stringwithDot[boost::bind(&HandlerT::AddHint, handler, ::_1)];
timestamp = (-qi::lit('&')) >> qi::lit("t") >> '=' >>
qi::uint_[boost::bind(&HandlerT::AddTimestamp, handler, ::_1)];
bearing = (-qi::lit('&')) >> qi::lit("b") >> '=' >>
(qi::int_ >> -(qi::lit(',') >> qi::int_ |
qi::attr(10)))[boost::bind<void>(add_bearing_wrapper, ::_1, ::_3)];
u = (-qi::lit('&')) >> qi::lit("u") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetUTurn, handler, ::_1)];
uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetAllUTurns, handler, ::_1)];
language = (-qi::lit('&')) >> qi::lit("hl") >> '=' >>
string[boost::bind(&HandlerT::SetLanguage, handler, ::_1)];
alt_route = (-qi::lit('&')) >> qi::lit("alt") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetAlternateRouteFlag, handler, ::_1)];
old_API = (-qi::lit('&')) >> qi::lit("geomformat") >> '=' >>
string[boost::bind(&HandlerT::SetDeprecatedAPIFlag, handler, ::_1)];
num_results = (-qi::lit('&')) >> qi::lit("num_results") >> '=' >>
qi::short_[boost::bind(&HandlerT::SetNumberOfResults, handler, ::_1)];
matching_beta = (-qi::lit('&')) >> qi::lit("matching_beta") >> '=' >>
qi::float_[boost::bind(&HandlerT::SetMatchingBeta, handler, ::_1)];
gps_precision = (-qi::lit('&')) >> qi::lit("gps_precision") >> '=' >>
qi::float_[boost::bind(&HandlerT::SetGPSPrecision, handler, ::_1)];
classify = (-qi::lit('&')) >> qi::lit("classify") >> '=' >>
qi::bool_[boost::bind(&HandlerT::SetClassify, handler, ::_1)];
locs = (-qi::lit('&')) >> qi::lit("locs") >> '=' >>
stringforPolyline[boost::bind(&HandlerT::SetCoordinatesFromGeometry, handler, ::_1)];
x = (-qi::lit('&')) >> qi::lit("tx") >> '=' >>
qi::int_[boost::bind<void>(set_x_wrapper, ::_1, ::_3)];
y = (-qi::lit('&')) >> qi::lit("ty") >> '=' >>
qi::int_[boost::bind<void>(set_y_wrapper, ::_1, ::_3)];
z = (-qi::lit('&')) >> qi::lit("tz") >> '=' >>
qi::int_[boost::bind<void>(set_z_wrapper, ::_1, ::_3)];
string = +(qi::char_("a-zA-Z"));
stringwithDot = +(qi::char_("a-zA-Z0-9_.-"));
stringwithPercent = +(qi::char_("a-zA-Z0-9_.-") | qi::char_('[') | qi::char_(']') |
(qi::char_('%') >> qi::char_("0-9A-Z") >> qi::char_("0-9A-Z")));
stringforPolyline = +(qi::char_("a-zA-Z0-9_.-[]{}@?|\\%~`^"));
}
qi::rule<Iterator> api_call, query, location_options, location_with_options,
destination_with_options, source_with_options, t_u, t_h, u_h, t_u_h;
qi::rule<Iterator, std::string()> service, zoom, output, string, jsonp, checksum, location,
destination, source, hint, timestamp, bearing, stringwithDot, stringwithPercent, language,
geometry, cmp, alt_route, u, uturns, old_API, num_results, matching_beta, gps_precision,
classify, locs, instruction, stringforPolyline, x, y, z;
HandlerT *handler;
};
}
}
#endif /* API_GRAMMAR_HPP */

View File

@ -1,18 +1,14 @@
#ifndef REQUEST_HANDLER_HPP #ifndef REQUEST_HANDLER_HPP
#define REQUEST_HANDLER_HPP #define REQUEST_HANDLER_HPP
#include "server/service_handler.hpp"
#include <string> #include <string>
namespace osrm namespace osrm
{ {
class OSRM;
namespace engine
{
struct RouteParameters;
}
namespace server namespace server
{ {
template <typename Iterator, class HandlerT> struct APIGrammar;
namespace http namespace http
{ {
@ -24,17 +20,16 @@ class RequestHandler
{ {
public: public:
using APIGrammarParser = APIGrammar<std::string::iterator, engine::RouteParameters>;
RequestHandler(); RequestHandler();
RequestHandler(const RequestHandler &) = delete; RequestHandler(const RequestHandler &) = delete;
RequestHandler &operator=(const RequestHandler &) = delete; RequestHandler &operator=(const RequestHandler &) = delete;
void handle_request(const http::request &current_request, http::reply &current_reply); void RegisterServiceHandler(std::unique_ptr<ServiceHandler> service_handler);
void RegisterRoutingMachine(OSRM *osrm);
void HandleRequest(const http::request &current_request, http::reply &current_reply);
private: private:
OSRM *routing_machine; std::unique_ptr<ServiceHandler> service_handler;
}; };
} }
} }

View File

@ -3,6 +3,7 @@
#include "server/connection.hpp" #include "server/connection.hpp"
#include "server/request_handler.hpp" #include "server/request_handler.hpp"
#include "server/service_handler.hpp"
#include "util/integer_range.hpp" #include "util/integer_range.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
@ -76,7 +77,10 @@ class Server
void Stop() { io_service.stop(); } void Stop() { io_service.stop(); }
RequestHandler &GetRequestHandlerPtr() { return request_handler; } void RegisterServiceHandler(std::unique_ptr<ServiceHandler> service_handler_)
{
request_handler.RegisterServiceHandler(std::move(service_handler_));
}
private: private:
void HandleAccept(const boost::system::error_code &e) void HandleAccept(const boost::system::error_code &e)

View File

@ -0,0 +1,36 @@
#ifndef SERVER_SERVICE_BASE_SERVICE_HPP
#define SERVER_SERVICE_BASE_SERVICE_HPP
#include "engine/status.hpp"
#include "util/coordinate.hpp"
#include "osrm/osrm.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace server
{
namespace service
{
class BaseService
{
public:
BaseService(OSRM &routing_machine) : routing_machine(routing_machine) {}
virtual ~BaseService() = default;
virtual engine::Status RunQuery(std::vector<util::FixedPointCoordinate> coordinates,
std::string &options,
util::json::Object &json_result) = 0;
virtual unsigned GetVersion() = 0;
protected:
OSRM &routing_machine;
};
}
}
}
#endif

View File

@ -0,0 +1,34 @@
#ifndef SERVER_SERVICE_ROUTE_SERVICE_HPP
#define SERVER_SERVICE_ROUTE_SERVICE_HPP
#include "server/service/base_service.hpp"
#include "engine/status.hpp"
#include "util/coordinate.hpp"
#include "osrm/osrm.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace server
{
namespace service
{
class RouteService final : public BaseService
{
public:
RouteService(OSRM& routing_machine) : BaseService(routing_machine) {}
virtual engine::Status RunQuery(std::vector<util::FixedPointCoordinate> coordinates,
std::string &options,
util::json::Object &json_result) final override;
virtual unsigned GetVersion() final override { return 1; }
};
}
}
}
#endif

View File

@ -0,0 +1,40 @@
#ifndef SERVER_SERVICE_HANLDER_HPP
#define SERVER_SERVICE_HANLDER_HPP
#include "server/service/base_service.hpp"
#include "osrm/osrm.hpp"
#include <unordered_map>
namespace osrm
{
namespace util
{
namespace json
{
struct Object;
}
}
namespace server
{
namespace api
{
struct ParsedURL;
}
class ServiceHandler
{
public:
ServiceHandler(osrm::EngineConfig &config);
engine::Status RunQuery(api::ParsedURL parsed_url, util::json::Object &json_result);
private:
std::unordered_map<std::string, std::unique_ptr<service::BaseService>> service_map;
OSRM routing_machine;
};
}
}
#endif

View File

@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace osrm namespace osrm
{ {
constexpr const double COORDINATE_PRECISION = 1000000.0; constexpr const double COORDINATE_PRECISION = 1e6;
namespace util namespace util
{ {
@ -52,6 +52,7 @@ struct FixedPointCoordinate
FixedPointCoordinate(const T &coordinate) FixedPointCoordinate(const T &coordinate)
: lat(coordinate.lat), lon(coordinate.lon) : lat(coordinate.lat), lon(coordinate.lon)
{ {
static_assert(!std::is_same<T, FixedPointCoordinate>::value, "This constructor should not be used for FixedPointCoordinates");
static_assert(std::is_same<decltype(lat), decltype(coordinate.lat)>::value, static_assert(std::is_same<decltype(lat), decltype(coordinate.lat)>::value,
"coordinate types incompatible"); "coordinate types incompatible");
static_assert(std::is_same<decltype(lon), decltype(coordinate.lon)>::value, static_assert(std::is_same<decltype(lon), decltype(coordinate.lon)>::value,
@ -59,11 +60,13 @@ struct FixedPointCoordinate
} }
bool IsValid() const; bool IsValid() const;
bool operator==(const FixedPointCoordinate &other) const; friend bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs);
friend std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate); friend bool operator!=(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs);
friend std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate);
}; };
std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate); bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs);
std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate);
} }
} }

View File

@ -59,7 +59,7 @@ template <> Array make_array(const std::vector<bool> &vector)
} }
// Easy acces to object hierachies // Easy acces to object hierachies
Value &get(Value &value) { return value; } inline Value &get(Value &value) { return value; }
template <typename... Keys> Value &get(Value &value, const char *key, Keys... keys) template <typename... Keys> Value &get(Value &value, const char *key, Keys... keys)
{ {

85
include/util/tiles.hpp Normal file
View File

@ -0,0 +1,85 @@
#ifndef UTIL_TILES_HPP
#define UTIL_TILES_HPP
#include <boost/assert.hpp>
#include <cmath>
#include <tuple>
// This is a port of the tilebelt algorithm https://github.com/mapbox/tilebelt
namespace osrm
{
namespace util
{
namespace tiles
{
struct Tile
{
unsigned x;
unsigned y;
unsigned z;
};
namespace detail
{
// optimized for 32bit integers
static constexpr unsigned MAX_ZOOM = 32;
// Returns 1-indexed 1..32 of MSB if value > 0 or 0 if value == 0
inline unsigned getMSBPosition(std::uint32_t value)
{
if (value == 0)
return 0;
std::uint8_t pos = 1;
while (value >>= 1)
pos++;
return pos;
}
inline unsigned getBBMaxZoom(const Tile top_left, const Tile bottom_left)
{
auto x_xor = top_left.x ^ bottom_left.x;
auto y_xor = top_left.y ^ bottom_left.y;
auto lon_msb = detail::getMSBPosition(x_xor);
auto lat_msb = detail::getMSBPosition(y_xor);
return MAX_ZOOM - std::max(lon_msb, lat_msb);
}
}
inline Tile pointToTile(const double lon, const double lat)
{
auto sin_lat = std::sin(lat * M_PI / 180.);
auto p2z = std::pow(2, detail::MAX_ZOOM);
unsigned x = p2z * (lon / 360. + 0.5);
unsigned y = p2z * (0.5 - 0.25 * std::log((1 + sin_lat) / (1 - sin_lat)) / M_PI);
return Tile{x, y, detail::MAX_ZOOM};
}
inline Tile getBBMaxZoomTile(const double min_lon,
const double min_lat,
const double max_lon,
const double max_lat)
{
const auto top_left = pointToTile(min_lon, min_lat);
const auto bottom_left = pointToTile(max_lon, max_lat);
BOOST_ASSERT(top_left.z == detail::MAX_ZOOM);
BOOST_ASSERT(bottom_left.z == detail::MAX_ZOOM);
const auto max_zoom = detail::getBBMaxZoom(top_left, bottom_left);
if (max_zoom == 0)
{
return Tile{0, 0, 0};
}
auto x = top_left.x >> (detail::MAX_ZOOM - max_zoom);
auto y = top_left.y >> (detail::MAX_ZOOM - max_zoom);
return Tile{x, y, max_zoom};
}
}
}
}
#endif

View File

@ -0,0 +1,222 @@
#include "engine/api/json_factory.hpp"
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/step_maneuver.hpp"
#include "engine/guidance/route_leg.hpp"
#include "engine/guidance/route.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/polyline_compressor.hpp"
#include "engine/hint.hpp"
#include <boost/assert.hpp>
#include <boost/range/irange.hpp>
#include <string>
#include <utility>
#include <algorithm>
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
namespace json
{
namespace detail
{
std::string instructionToString(extractor::TurnInstruction instruction)
{
std::string token;
// FIXME this could be an array.
switch (instruction)
{
case extractor::TurnInstruction::GoStraight:
token = "continue";
break;
case extractor::TurnInstruction::TurnSlightRight:
token = "bear right";
break;
case extractor::TurnInstruction::TurnRight:
token = "right";
break;
case extractor::TurnInstruction::TurnSharpRight:
token = "sharp right";
break;
case extractor::TurnInstruction::UTurn:
token = "uturn";
break;
case extractor::TurnInstruction::TurnSharpLeft:
token = "sharp left";
break;
case extractor::TurnInstruction::TurnLeft:
token = "left";
break;
case extractor::TurnInstruction::TurnSlightLeft:
token = "bear left";
break;
case extractor::TurnInstruction::HeadOn:
token = "head on";
break;
case extractor::TurnInstruction::EnterRoundAbout:
token = "enter roundabout";
break;
case extractor::TurnInstruction::LeaveRoundAbout:
token = "leave roundabout";
break;
case extractor::TurnInstruction::StayOnRoundAbout:
token = "stay on roundabout";
break;
case extractor::TurnInstruction::StartAtEndOfStreet:
token = "depart";
break;
case extractor::TurnInstruction::ReachedYourDestination:
token = "arrive";
break;
case extractor::TurnInstruction::NameChanges:
token = "name changed";
break;
case extractor::TurnInstruction::NoTurn:
case extractor::TurnInstruction::ReachViaLocation:
case extractor::TurnInstruction::EnterAgainstAllowedDirection:
case extractor::TurnInstruction::LeaveAgainstAllowedDirection:
case extractor::TurnInstruction::InverseAccessRestrictionFlag:
case extractor::TurnInstruction::AccessRestrictionFlag:
case extractor::TurnInstruction::AccessRestrictionPenalty:
BOOST_ASSERT_MSG(false, "Invalid turn type used");
break;
}
return token;
}
util::json::Array coordinateToLonLat(const FixedPointCoordinate &coordinate)
{
util::json::Array array;
array.values.push_back(coordinate.lon / COORDINATE_PRECISION);
array.values.push_back(coordinate.lat / COORDINATE_PRECISION);
return array;
}
util::json::Array coordinateToLatLon(const FixedPointCoordinate &coordinate)
{
util::json::Array array;
array.values.push_back(coordinate.lat / COORDINATE_PRECISION);
array.values.push_back(coordinate.lon / COORDINATE_PRECISION);
return array;
}
// FIXME this actually needs to be configurable from the profiles
std::string modeToString(const extractor::TravelMode mode)
{
std::string token;
switch (mode)
{
case TRAVEL_MODE_DEFAULT:
token = "default";
break;
case TRAVEL_MODE_INACCESSIBLE:
token = "inaccessible";
break;
default:
token = "other";
break;
}
return token;
}
} // namespace detail
util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
{
util::json::Object step_maneuver;
step_maneuver.values["type"] = detail::instructionToString(maneuver.instruction);
step_maneuver.values["location"] = detail::coordinateToLatLon(maneuver.location);
step_maneuver.values["heading_before"] = maneuver.heading_before;
step_maneuver.values["heading_after"] = maneuver.heading_after;
return step_maneuver;
}
util::json::Object makeRouteStep(guidance::RouteStep &&step,
boost::optional<util::json::Value> geometry)
{
util::json::Object route_step;
route_step.values["distance"] = step.distance;
route_step.values["duration"] = step.duration;
route_step.values["way_name"] = std::move(step.way_name);
route_step.values["mode"] = detail::modeToString(step.mode);
route_step.values["maneuver"] = makeStepManeuver(step.maneuver);
if (geometry)
{
route_step.values["geometry"] = std::move(*geometry);
}
return route_step;
}
util::json::Object makeRoute(const guidance::Route &route,
util::json::Array &&legs,
boost::optional<util::json::Value> geometry)
{
util::json::Object json_route;
json_route.values["distance"] = route.distance;
json_route.values["duration"] = route.duration;
json_route.values["legs"] = std::move(legs);
if (geometry)
{
json_route.values["geometry"] = std::move(*geometry);
}
return json_route;
}
util::json::Object
makeWaypoint(const FixedPointCoordinate location, std::string &&way_name, const Hint &hint)
{
util::json::Object waypoint;
waypoint.values["location"] = detail::coordinateToLatLon(location);
waypoint.values["way_name"] = std::move(way_name);
waypoint.values["hint"] = hint.ToBase64();
return waypoint;
}
util::json::Object makeRouteLeg(guidance::RouteLeg &&leg, util::json::Array &&steps)
{
util::json::Object route_leg;
route_leg.values["distance"] = leg.distance;
route_leg.values["duration"] = leg.duration;
route_leg.values["summary"] = std::move(leg.summary);
route_leg.values["steps"] = std::move(steps);
return route_leg;
}
util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> &&legs,
const std::vector<guidance::LegGeometry> &leg_geometries)
{
util::json::Array json_legs;
for (const auto idx : boost::irange(0UL, legs.size()))
{
auto &&leg = std::move(legs[idx]);
const auto &leg_geometry = leg_geometries[idx];
util::json::Array json_steps;
json_steps.values.reserve(leg.steps.size());
const auto begin_iter = leg_geometry.locations.begin();
std::transform(
std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()),
std::back_inserter(json_steps.values), [&begin_iter](guidance::RouteStep &&step)
{
// FIXME we only support polyline here
auto geometry = boost::make_optional<util::json::Value>(
makePolyline(begin_iter + step.geometry_begin, begin_iter + step.geometry_end));
return makeRouteStep(std::move(step), std::move(geometry));
});
json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));
}
return json_legs;
}
}
}
} // namespace engine
} // namespace osrm

View File

@ -1,8 +1,9 @@
#include "engine/douglas_peucker.hpp" #include "engine/douglas_peucker.hpp"
#include "util/coordinate_calculation.hpp" #include "util/coordinate_calculation.hpp"
#include "util/coordinate.hpp"
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include "osrm/coordinate.hpp" #include <boost/range/irange.hpp>
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
@ -17,8 +18,12 @@ namespace engine
namespace namespace
{ {
// FIXME This algorithm is a very naive approximation that leads to
// problems like (almost) co-linear points not being simplified.
// Switch to real-point-segment distance of projected coordinates
struct CoordinatePairCalculator struct CoordinatePairCalculator
{ {
CoordinatePairCalculator() = delete;
CoordinatePairCalculator(const util::FixedPointCoordinate coordinate_a, CoordinatePairCalculator(const util::FixedPointCoordinate coordinate_a,
const util::FixedPointCoordinate coordinate_b) const util::FixedPointCoordinate coordinate_b)
{ {
@ -56,44 +61,29 @@ struct CoordinatePairCalculator
}; };
} }
void douglasPeucker(std::vector<SegmentInformation>::iterator begin, std::vector<util::FixedPointCoordinate>
std::vector<SegmentInformation>::iterator end, douglasPeucker(std::vector<util::FixedPointCoordinate>::const_iterator begin,
std::vector<util::FixedPointCoordinate>::const_iterator end,
const unsigned zoom_level) const unsigned zoom_level)
{ {
using Iter = decltype(begin); BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE,
using GeometryRange = std::pair<Iter, Iter>; "unsupported zoom level");
std::stack<GeometryRange> recursion_stack;
const auto size = std::distance(begin, end); const auto size = std::distance(begin, end);
if (size < 2) if (size < 2)
{ {
return; return {};
} }
begin->necessary = true; std::vector<bool> is_necessary(size, false);
std::prev(end)->necessary = true; BOOST_ASSERT(is_necessary.size() >= 2);
is_necessary.front() = true;
is_necessary.back() = true;
using GeometryRange = std::pair<std::size_t, std::size_t>;
{ std::stack<GeometryRange> recursion_stack;
BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE,
"unsupported zoom level"); recursion_stack.emplace(0UL, size-1);
auto left_border = begin;
auto right_border = std::next(begin);
// Sweep over array and identify those ranges that need to be checked
do
{
// traverse list until new border element found
if (right_border->necessary)
{
// sanity checks
BOOST_ASSERT(left_border->necessary);
BOOST_ASSERT(right_border->necessary);
recursion_stack.emplace(left_border, right_border);
left_border = right_border;
}
++right_border;
} while (right_border != end);
}
// mark locations as 'necessary' by divide-and-conquer // mark locations as 'necessary' by divide-and-conquer
while (!recursion_stack.empty()) while (!recursion_stack.empty())
@ -102,25 +92,24 @@ void douglasPeucker(std::vector<SegmentInformation>::iterator begin,
const GeometryRange pair = recursion_stack.top(); const GeometryRange pair = recursion_stack.top();
recursion_stack.pop(); recursion_stack.pop();
// sanity checks // sanity checks
BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary"); BOOST_ASSERT_MSG(is_necessary[pair.first], "left border must be necessary");
BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary"); BOOST_ASSERT_MSG(is_necessary[pair.second], "right border must be necessary");
BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry"); BOOST_ASSERT_MSG(pair.second < size, "right border outside of geometry");
BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0, BOOST_ASSERT_MSG(pair.first <= pair.second, "left border on the wrong side");
"left border on the wrong side");
int max_int_distance = 0; int max_int_distance = 0;
auto farthest_entry_it = pair.second; auto farthest_entry_index = pair.second;
const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location); const CoordinatePairCalculator dist_calc(begin[pair.first], begin[pair.second]);
// sweep over range to find the maximum // sweep over range to find the maximum
for (auto it = std::next(pair.first); it != pair.second; ++it) for (auto idx = pair.first + 1; idx != pair.second; ++idx)
{ {
const int distance = dist_calc(it->location); const int distance = dist_calc(begin[idx]);
// found new feasible maximum? // found new feasible maximum?
if (distance > max_int_distance && if (distance > max_int_distance &&
distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
{ {
farthest_entry_it = it; farthest_entry_index = idx;
max_int_distance = distance; max_int_distance = distance;
} }
} }
@ -129,17 +118,29 @@ void douglasPeucker(std::vector<SegmentInformation>::iterator begin,
if (max_int_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) if (max_int_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
{ {
// mark idx as necessary // mark idx as necessary
farthest_entry_it->necessary = true; is_necessary[farthest_entry_index] = true;
if (1 < std::distance(pair.first, farthest_entry_it)) if (pair.first < farthest_entry_index)
{ {
recursion_stack.emplace(pair.first, farthest_entry_it); recursion_stack.emplace(pair.first, farthest_entry_index);
} }
if (1 < std::distance(farthest_entry_it, pair.second)) if (farthest_entry_index < pair.second)
{ {
recursion_stack.emplace(farthest_entry_it, pair.second); recursion_stack.emplace(farthest_entry_index, pair.second);
} }
} }
} }
auto simplified_size = std::count(is_necessary.begin(), is_necessary.end(), true);
std::vector<util::FixedPointCoordinate> simplified_geometry;
simplified_geometry.reserve(simplified_size);
for (auto idx : boost::irange<std::size_t>(0UL, size))
{
if (is_necessary[idx])
{
simplified_geometry.push_back(begin[idx]);
}
}
return simplified_geometry;
} }
} // ns engine } // ns engine
} // ns osrm } // ns osrm

View File

@ -1,15 +1,16 @@
#include "engine/engine.hpp" #include "engine/engine.hpp"
#include "engine/engine_config.hpp" #include "engine/engine_config.hpp"
#include "engine/route_parameters.hpp" #include "engine/api/route_parameters.hpp"
#include "engine/status.hpp"
#include "engine/plugins/distance_table.hpp" //#include "engine/plugins/distance_table.hpp"
#include "engine/plugins/hello_world.hpp" //#include "engine/plugins/hello_world.hpp"
#include "engine/plugins/nearest.hpp" //#include "engine/plugins/nearest.hpp"
#include "engine/plugins/timestamp.hpp" //#include "engine/plugins/timestamp.hpp"
#include "engine/plugins/trip.hpp" //#include "engine/plugins/trip.hpp"
#include "engine/plugins/viaroute.hpp" #include "engine/plugins/viaroute.hpp"
#include "engine/plugins/match.hpp" //#include "engine/plugins/match.hpp"
#include "engine/plugins/tile.hpp" //#include "engine/plugins/tile.hpp"
#include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/datafacade_base.hpp"
#include "engine/datafacade/internal_datafacade.hpp" #include "engine/datafacade/internal_datafacade.hpp"
@ -35,120 +36,93 @@ namespace osrm
namespace engine namespace engine
{ {
// Abstracted away the query locking into a template function
// Works the same for every plugin.
template<typename ParameterT, typename PluginT>
Status RunQuery(const std::unique_ptr<Engine::EngineLock> &lock,
datafacade::BaseDataFacade &facade,
const ParameterT &parameters,
PluginT &plugin,
util::json::Object &json_result)
{
if (!lock)
{
return plugin.HandleRequest(parameters, json_result);
}
BOOST_ASSERT(lock);
lock->IncreaseQueryCount();
auto& shared_facade = static_cast<datafacade::SharedDataFacade &>(facade);
shared_facade.CheckAndReloadFacade();
// Get a shared data lock so that other threads won't update
// things while the query is running
boost::shared_lock<boost::shared_mutex> data_lock{shared_facade.data_mutex};
Status status = plugin.HandleRequest(parameters, json_result);
lock->DecreaseQueryCount();
return status;
}
Engine::Engine(EngineConfig &config) Engine::Engine(EngineConfig &config)
{ {
if (config.use_shared_memory) if (config.use_shared_memory)
{ {
barrier = util::make_unique<storage::SharedBarriers>(); lock = util::make_unique<EngineLock>();
query_data_facade = new datafacade::SharedDataFacade<contractor::QueryEdge::EdgeData>(); query_data_facade = util::make_unique<datafacade::SharedDataFacade>();
} }
else else
{ {
// populate base path // populate base path
util::populate_base_path(config.server_paths); util::populate_base_path(config.server_paths);
query_data_facade = new datafacade::InternalDataFacade<contractor::QueryEdge::EdgeData>( query_data_facade = util::make_unique<datafacade::InternalDataFacade>(config.server_paths);
config.server_paths);
} }
using DataFacade = datafacade::BaseDataFacade<contractor::QueryEdge::EdgeData>; route_plugin = util::make_unique<plugins::ViaRoutePlugin>(*query_data_facade, config.max_locations_viaroute);
// The following plugins handle all requests.
RegisterPlugin(new plugins::DistanceTablePlugin<DataFacade>(
query_data_facade, config.max_locations_distance_table));
RegisterPlugin(new plugins::HelloWorldPlugin());
RegisterPlugin(new plugins::NearestPlugin<DataFacade>(query_data_facade));
RegisterPlugin(new plugins::MapMatchingPlugin<DataFacade>(query_data_facade,
config.max_locations_map_matching));
RegisterPlugin(new plugins::TimestampPlugin<DataFacade>(query_data_facade));
RegisterPlugin(
new plugins::ViaRoutePlugin<DataFacade>(query_data_facade, config.max_locations_viaroute));
RegisterPlugin(
new plugins::RoundTripPlugin<DataFacade>(query_data_facade, config.max_locations_trip));
RegisterPlugin(new plugins::TilePlugin<DataFacade>(query_data_facade));
} }
void Engine::RegisterPlugin(plugins::BasePlugin *raw_plugin_ptr) Status Engine::Route(const api::RouteParameters &route_parameters,
util::json::Object &result)
{ {
std::unique_ptr<plugins::BasePlugin> plugin_ptr(raw_plugin_ptr); return RunQuery(lock, *query_data_facade, route_parameters, *route_plugin, result);
util::SimpleLogger().Write() << "loaded plugin: " << plugin_ptr->GetDescriptor();
plugin_map[plugin_ptr->GetDescriptor()] = std::move(plugin_ptr);
}
int Engine::RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result)
{
const auto &plugin_iterator = plugin_map.find(route_parameters.service);
if (plugin_map.end() == plugin_iterator)
{
json_result.values["status_message"] = "Service not found";
return 400;
}
osrm::engine::plugins::BasePlugin::Status return_code;
increase_concurrent_query_count();
if (barrier)
{
// Get a shared data lock so that other threads won't update
// things while the query is running
boost::shared_lock<boost::shared_mutex> data_lock{
(static_cast<datafacade::SharedDataFacade<contractor::QueryEdge::EdgeData> *>(
query_data_facade))->data_mutex};
return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result);
}
else
{
return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result);
}
decrease_concurrent_query_count();
return static_cast<int>(return_code);
} }
// decrease number of concurrent queries // decrease number of concurrent queries
void Engine::decrease_concurrent_query_count() void Engine::EngineLock::DecreaseQueryCount()
{ {
if (!barrier)
{
return;
}
// lock query // lock query
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> query_lock( boost::interprocess::scoped_lock<boost::interprocess::named_mutex> query_lock(
barrier->query_mutex); barrier.query_mutex);
// decrement query count // decrement query count
--(barrier->number_of_queries); --(barrier.number_of_queries);
BOOST_ASSERT_MSG(0 <= barrier->number_of_queries, "invalid number of queries"); BOOST_ASSERT_MSG(0 <= barrier.number_of_queries, "invalid number of queries");
// notify all processes that were waiting for this condition // notify all processes that were waiting for this condition
if (0 == barrier->number_of_queries) if (0 == barrier.number_of_queries)
{ {
barrier->no_running_queries_condition.notify_all(); barrier.no_running_queries_condition.notify_all();
} }
} }
// increase number of concurrent queries // increase number of concurrent queries
void Engine::increase_concurrent_query_count() void Engine::EngineLock::IncreaseQueryCount()
{ {
if (!barrier)
{
return;
}
// lock update pending // lock update pending
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> pending_lock( boost::interprocess::scoped_lock<boost::interprocess::named_mutex> pending_lock(
barrier->pending_update_mutex); barrier.pending_update_mutex);
// lock query // lock query
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> query_lock( boost::interprocess::scoped_lock<boost::interprocess::named_mutex> query_lock(
barrier->query_mutex); barrier.query_mutex);
// unlock update pending // unlock update pending
pending_lock.unlock(); pending_lock.unlock();
// increment query count // increment query count
++(barrier->number_of_queries); ++(barrier.number_of_queries);
(static_cast<datafacade::SharedDataFacade<contractor::QueryEdge::EdgeData> *>(
query_data_facade))->CheckAndReloadFacade();
} }
} }
} }

View File

@ -0,0 +1,99 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#include "engine/guidance/leg_geometry.hpp"
#include "engine/douglas_peucker.hpp"
#include "util/tiles.hpp"
#include <vector>
#include <tuple>
namespace osrm
{
namespace engine
{
namespace guidance
{
namespace
{
unsigned calculateOverviewZoomLevel(const std::vector<LegGeometry> &leg_geometries)
{
int min_lon = std::numeric_limits<int>::max();
int min_lat = std::numeric_limits<int>::max();
int max_lon = -std::numeric_limits<int>::max();
int max_lat = -std::numeric_limits<int>::max();
for (const auto &leg_geometry : leg_geometries)
{
for (const auto coord : leg_geometry.locations)
{
min_lon = std::min(min_lon, coord.lon);
max_lon = std::max(max_lon, coord.lon);
min_lat = std::min(min_lat, coord.lat);
max_lat = std::max(max_lat, coord.lat);
}
}
return util::tiles::getBBMaxZoomTile(min_lon, min_lat, max_lon, max_lat).z;
}
std::vector<util::FixedPointCoordinate> simplifyGeometry(const std::vector<LegGeometry> &leg_geometries, const unsigned zoom_level)
{
std::vector<util::FixedPointCoordinate> overview_geometry;
auto leg_index = 0UL;
for (const auto geometry : leg_geometries)
{
auto simplified_geometry =
douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level);
// not the last leg
if (leg_index < leg_geometries.size() - 1)
{
simplified_geometry.pop_back();
}
overview_geometry.insert(overview_geometry.end(), simplified_geometry.begin(),
simplified_geometry.end());
}
return overview_geometry;
}
}
std::vector<util::FixedPointCoordinate>
assembleOverview(const std::vector<LegGeometry> &leg_geometries, const bool use_simplification)
{
if (use_simplification)
{
const auto zoom_level = calculateOverviewZoomLevel(leg_geometries);
return simplifyGeometry(leg_geometries, zoom_level);
}
BOOST_ASSERT(!use_simplification);
auto overview_size = std::accumulate(leg_geometries.begin(), leg_geometries.end(), 0,
[](const std::size_t sum, const LegGeometry &leg_geometry)
{
return sum + leg_geometry.locations.size();
}) -
leg_geometries.size() + 1;
std::vector<util::FixedPointCoordinate> overview_geometry;
overview_geometry.reserve(overview_size);
auto leg_index = 0UL;
for (const auto geometry : leg_geometries)
{
auto begin = geometry.locations.begin();
auto end = geometry.locations.end();
if (leg_index < leg_geometries.size() - 1)
{
end = std::prev(end);
}
overview_geometry.insert(overview_geometry.end(), begin, end);
}
return overview_geometry;
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif

View File

@ -0,0 +1,115 @@
#include "engine/plugins/viaroute.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/api/route_api.hpp"
#include "engine/object_encoder.hpp"
#include "engine/status.hpp"
#include "util/for_each_pair.hpp"
#include "util/integer_range.hpp"
#include "util/json_container.hpp"
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
namespace osrm
{
namespace engine
{
namespace plugins
{
ViaRoutePlugin::ViaRoutePlugin(datafacade::BaseDataFacade &facade_, int max_locations_viaroute)
: BasePlugin(facade_), shortest_path(&facade_, heaps), alternative_path(&facade_, heaps),
direct_shortest_path(&facade_, heaps), max_locations_viaroute(max_locations_viaroute)
{
}
Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameters,
util::json::Object &json_result)
{
if (max_locations_viaroute > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
{
return Error("too-big",
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" +
std::to_string(max_locations_viaroute) + ")",
json_result);
}
if (!CheckAllCoordinates(route_parameters.coordinates))
{
return Error("invalid-value", "Invalid coordinate value.", json_result);
}
auto phantom_node_pairs = GetPhantomNodes(route_parameters);
if (phantom_node_pairs.size() != route_parameters.coordinates.size())
{
return Error("no-segment",
std::string("Could not find a matching segment for coordinate ") +
std::to_string(phantom_node_pairs.size()),
json_result);
}
BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size());
auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs);
InternalRouteResult raw_route;
auto build_phantom_pairs = [&raw_route](const PhantomNode &first_node,
const PhantomNode &second_node)
{
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
};
util::for_each_pair(snapped_phantoms, build_phantom_pairs);
if (1 == raw_route.segment_end_coordinates.size())
{
if (route_parameters.alternative && facade.GetCoreSize() == 0)
{
alternative_path(raw_route.segment_end_coordinates.front(), raw_route);
}
else
{
direct_shortest_path(raw_route.segment_end_coordinates, raw_route);
}
}
else
{
shortest_path(raw_route.segment_end_coordinates, route_parameters.uturns, raw_route);
}
// we can only know this after the fact, different SCC ids still
// allow for connection in one direction.
if (raw_route.is_valid())
{
api::RouteAPI route_api{BasePlugin::facade, route_parameters};
route_api.MakeResponse(raw_route, json_result);
}
else
{
auto first_component_id = snapped_phantoms.front().component.id;
auto not_in_same_component = std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
[first_component_id](const PhantomNode &node)
{
return node.component.id != first_component_id;
});
if (not_in_same_component)
{
return Error("no-route", "Impossible route between points", json_result);
}
else
{
return Error("no-route", "No route found between points", json_result);
}
}
return Status::Ok;
}
}
}
}

View File

@ -1,8 +1,10 @@
#include "engine/polyline_compressor.hpp" #include "engine/polyline_compressor.hpp"
#include <boost/assert.hpp>
#include <cstddef> #include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cmath> #include <cmath>
#include <algorithm>
namespace osrm namespace osrm
{ {
@ -55,31 +57,30 @@ std::string encode(std::vector<int> &numbers)
} }
} // anonymous ns } // anonymous ns
std::string polylineEncode(const std::vector<SegmentInformation> &polyline)
std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end)
{ {
if (polyline.empty()) auto size = std::distance(begin, end);
if (size == 0)
{ {
return {}; return {};
} }
std::vector<int> delta_numbers; std::vector<int> delta_numbers;
delta_numbers.reserve((polyline.size() - 1) * 2); BOOST_ASSERT(size > 0);
delta_numbers.reserve((size - 1) * 2);
util::FixedPointCoordinate previous_coordinate = {0, 0}; util::FixedPointCoordinate previous_coordinate = {0, 0};
for (const auto &segment : polyline) std::for_each(begin, end, [&delta_numbers, &previous_coordinate](const FixedPointCoordinate loc)
{ {
if (segment.necessary) const int lat_diff = (loc.lat - previous_coordinate.lat) * detail::COORDINATE_TO_POLYLINE;
{ const int lon_diff = (loc.lon - previous_coordinate.lon) * detail::COORDINATE_TO_POLYLINE;
const int lat_diff = segment.location.lat - previous_coordinate.lat;
const int lon_diff = segment.location.lon - previous_coordinate.lon;
delta_numbers.emplace_back(lat_diff); delta_numbers.emplace_back(lat_diff);
delta_numbers.emplace_back(lon_diff); delta_numbers.emplace_back(lon_diff);
previous_coordinate = segment.location; previous_coordinate = loc;
} });
}
return encode(delta_numbers); return encode(delta_numbers);
} }
std::vector<util::FixedPointCoordinate> decodePolyline(const std::string &geometry_string)
std::vector<util::FixedPointCoordinate> polylineDecode(const std::string &geometry_string)
{ {
std::vector<util::FixedPointCoordinate> new_coordinates; std::vector<util::FixedPointCoordinate> new_coordinates;
int index = 0, len = geometry_string.size(); int index = 0, len = geometry_string.size();
@ -109,8 +110,8 @@ std::vector<util::FixedPointCoordinate> polylineDecode(const std::string &geomet
lng += dlng; lng += dlng;
util::FixedPointCoordinate p; util::FixedPointCoordinate p;
p.lat = COORDINATE_PRECISION * (((double)lat / 1E6)); p.lat = lat * detail::POLYLINE_TO_COORDINATE;
p.lon = COORDINATE_PRECISION * (((double)lng / 1E6)); p.lon = lng * detail::POLYLINE_TO_COORDINATE;
new_coordinates.push_back(p); new_coordinates.push_back(p);
} }

View File

@ -1,31 +0,0 @@
#include "engine/polyline_formatter.hpp"
#include "engine/polyline_compressor.hpp"
#include "osrm/coordinate.hpp"
namespace osrm
{
namespace engine
{
util::json::String polylineEncodeAsJSON(const std::vector<SegmentInformation> &polyline)
{
return util::json::String(polylineEncode(polyline));
}
util::json::Array polylineUnencodedAsJSON(const std::vector<SegmentInformation> &polyline)
{
util::json::Array json_geometry_array;
for (const auto &segment : polyline)
{
if (segment.necessary)
{
util::json::Array json_coordinate;
json_coordinate.values.push_back(segment.location.lat / COORDINATE_PRECISION);
json_coordinate.values.push_back(segment.location.lon / COORDINATE_PRECISION);
json_geometry_array.values.push_back(json_coordinate);
}
}
return json_geometry_array;
}
}
}

View File

@ -1,170 +0,0 @@
#include "engine/route_parameters.hpp"
#include "util/coordinate.hpp"
#include "engine/polyline_compressor.hpp"
#include <string>
#include <utility>
namespace osrm
{
namespace engine
{
RouteParameters::RouteParameters()
: zoom_level(18), print_instructions(false), alternate_route(true), geometry(true),
compression(true), deprecatedAPI(false), uturn_default(false), classify(false),
matching_beta(5), gps_precision(5), check_sum(-1), num_results(1)
{
}
void RouteParameters::SetZoomLevel(const short level)
{
if (18 >= level && 0 <= level)
{
zoom_level = level;
}
}
void RouteParameters::SetNumberOfResults(const short number)
{
if (number > 0 && number <= 100)
{
num_results = number;
}
}
void RouteParameters::SetAlternateRouteFlag(const bool flag) { alternate_route = flag; }
void RouteParameters::SetUTurn(const bool flag)
{
// the API grammar should make sure this never happens
BOOST_ASSERT(!uturns.empty());
uturns.back() = flag;
}
void RouteParameters::SetAllUTurns(const bool flag)
{
// if the flag flips the default, then we erase everything.
if (flag)
{
uturn_default = flag;
uturns.clear();
uturns.resize(coordinates.size(), uturn_default);
}
}
void RouteParameters::SetDeprecatedAPIFlag(const std::string & /*unused*/) { deprecatedAPI = true; }
void RouteParameters::SetChecksum(const unsigned sum) { check_sum = sum; }
void RouteParameters::SetInstructionFlag(const bool flag) { print_instructions = flag; }
void RouteParameters::SetService(const std::string &service_string) { service = service_string; }
void RouteParameters::SetClassify(const bool flag) { classify = flag; }
void RouteParameters::SetMatchingBeta(const double beta) { matching_beta = beta; }
void RouteParameters::SetGPSPrecision(const double precision) { gps_precision = precision; }
void RouteParameters::SetOutputFormat(const std::string &format) { output_format = format; }
void RouteParameters::SetJSONpParameter(const std::string &parameter)
{
jsonp_parameter = parameter;
}
void RouteParameters::AddHint(const std::string &hint)
{
hints.resize(coordinates.size());
if (!hints.empty())
{
hints.back() = hint;
}
}
void RouteParameters::AddTimestamp(const unsigned timestamp)
{
timestamps.resize(coordinates.size());
if (!timestamps.empty())
{
timestamps.back() = timestamp;
}
}
bool RouteParameters::AddBearing(int bearing, boost::optional<int> range)
{
if (bearing < 0 || bearing > 359)
return false;
if (range && (*range < 0 || *range > 180))
return false;
bearings.emplace_back(std::make_pair(bearing, range));
return true;
}
void RouteParameters::SetLanguage(const std::string &language_string)
{
language = language_string;
}
void RouteParameters::SetGeometryFlag(const bool flag) { geometry = flag; }
void RouteParameters::SetCompressionFlag(const bool flag) { compression = flag; }
void RouteParameters::AddCoordinate(const double latitude, const double longitude)
{
coordinates.emplace_back(static_cast<int>(COORDINATE_PRECISION * latitude),
static_cast<int>(COORDINATE_PRECISION * longitude));
is_source.push_back(true);
is_destination.push_back(true);
uturns.push_back(uturn_default);
}
void RouteParameters::AddDestination(const double latitude, const double longitude)
{
coordinates.emplace_back(static_cast<int>(COORDINATE_PRECISION * latitude),
static_cast<int>(COORDINATE_PRECISION * longitude));
is_source.push_back(false);
is_destination.push_back(true);
uturns.push_back(uturn_default);
}
void RouteParameters::AddSource(const double latitude, const double longitude)
{
coordinates.emplace_back(static_cast<int>(COORDINATE_PRECISION * latitude),
static_cast<int>(COORDINATE_PRECISION * longitude));
is_source.push_back(true);
is_destination.push_back(false);
uturns.push_back(uturn_default);
}
void RouteParameters::SetCoordinatesFromGeometry(const std::string &geometry_string)
{
coordinates = polylineDecode(geometry_string);
uturns.clear();
uturns.resize(coordinates.size(), uturn_default);
}
bool RouteParameters::SetX(const int x_)
{
x = x_;
return true;
}
bool RouteParameters::SetZ(const int z_)
{
if (z_ < 12)
{
return false;
}
z = z_;
return true;
}
bool RouteParameters::SetY(const int y_)
{
y = y_;
return true;
}
}
}

View File

@ -7,6 +7,13 @@ namespace osrm
namespace engine namespace engine
{ {
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3;
void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes) void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes)
{ {
if (forward_heap_1.get()) if (forward_heap_1.get())

View File

@ -1,7 +1,8 @@
#include "osrm/osrm.hpp" #include "osrm/osrm.hpp"
#include "engine/engine.hpp" #include "engine/engine.hpp"
#include "engine/status.hpp"
#include "engine/engine_config.hpp" #include "engine/engine_config.hpp"
#include "engine/plugins/plugin_base.hpp" #include "engine/plugins/viaroute.hpp"
#include "storage/shared_barriers.hpp" #include "storage/shared_barriers.hpp"
#include "util/make_unique.hpp" #include "util/make_unique.hpp"
@ -14,9 +15,9 @@ OSRM::OSRM(engine::EngineConfig &config_) : engine_(util::make_unique<engine::En
// needed because unique_ptr needs the size of OSRM_impl for delete // needed because unique_ptr needs the size of OSRM_impl for delete
OSRM::~OSRM() {} OSRM::~OSRM() {}
int OSRM::RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result) engine::Status OSRM::Route(const RouteParameters &route_parameters, util::json::Object &json_result)
{ {
return engine_->RunQuery(route_parameters, json_result); return engine_->Route(route_parameters, json_result);
} }
} }

View File

@ -0,0 +1,28 @@
#include "server/api/route_parameters_parser.hpp"
#include "server/api/route_parameters_grammar.hpp"
namespace osrm
{
namespace server
{
namespace api
{
boost::optional<engine::api::RouteParameters> parseRouteParameters(std::string::iterator& iter, std::string::iterator end)
{
RouteParametersGrammar grammar;
const auto result = boost::spirit::qi::parse(iter, end, grammar);
boost::optional<engine::api::RouteParameters> parameters;
if (result && iter == end)
{
parameters = std::move(grammar.route_parameters);
}
return parameters;
}
}
}
}

View File

@ -0,0 +1,104 @@
#include "server/api/url_parser.hpp"
#include "engine/polyline_compressor.hpp"
#include <boost/bind.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_uint.hpp>
#include <boost/spirit/include/qi_real.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_as_string.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_plus.hpp>
namespace osrm
{
namespace server
{
namespace api
{
namespace
{
namespace qi = boost::spirit::qi;
using Iterator = std::string::iterator;
struct URLGrammar : boost::spirit::qi::grammar<Iterator>
{
URLGrammar() : URLGrammar::base_type(url_rule)
{
const auto set_service = [this](std::string &service)
{
parsed_url.service = std::move(service);
};
const auto set_version = [this](const unsigned version)
{
parsed_url.version = version;
};
const auto set_profile = [this](std::string &profile)
{
parsed_url.profile = std::move(profile);
};
const auto set_options = [this](std::string &options)
{
parsed_url.options = std::move(options);
};
const auto add_coordinate = [this](const boost::fusion::vector<double, double> &lonLat)
{
parsed_url.coordinates.emplace_back(
util::FixedPointCoordinate(boost::fusion::at_c<1>(lonLat) * COORDINATE_PRECISION,
boost::fusion::at_c<0>(lonLat) * COORDINATE_PRECISION));
};
const auto polyline_to_coordinates = [this](const std::string &polyline)
{
parsed_url.coordinates = engine::decodePolyline(polyline);
};
alpha_numeral = qi::char_("a-zA-Z0-9");
polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^");
all_chars = polyline_chars | qi::char_("=,;:&");
polyline_rule = qi::as_string[qi::lit("polyline(") >> +polyline_chars >>
qi::lit(")")][polyline_to_coordinates];
location_rule = (qi::double_ >> qi::lit(',') >> qi::double_)[add_coordinate];
query_rule = (location_rule % ';') | polyline_rule;
service_rule = +alpha_numeral;
version_rule = qi::uint_;
profile_rule = +alpha_numeral;
options_rule = *all_chars;
url_rule = qi::lit('/') >> service_rule[set_service] >> qi::lit('/') >> qi::lit('v') >>
version_rule[set_version] >> qi::lit('/') >> profile_rule[set_profile] >>
qi::lit('/') >> query_rule >> -(qi::lit('?') >> options_rule[set_options]);
}
ParsedURL parsed_url;
qi::rule<Iterator> url_rule;
qi::rule<Iterator, std::string()> options_rule, service_rule, profile_rule;
qi::rule<Iterator> query_rule, polyline_rule, location_rule;
qi::rule<Iterator, unsigned()> version_rule;
qi::rule<Iterator, char()> alpha_numeral, all_chars, polyline_chars;
};
}
boost::optional<ParsedURL> parseURL(std::string::iterator &iter, std::string::iterator end)
{
boost::optional<ParsedURL> parsed_url;
URLGrammar grammar;
const auto result = boost::spirit::qi::parse(iter, end, grammar);
if (result && iter == end)
{
parsed_url = std::move(grammar.parsed_url);
}
return parsed_url;
}
}
}
}

View File

@ -50,7 +50,7 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
if (result == RequestParser::RequestStatus::valid) if (result == RequestParser::RequestStatus::valid)
{ {
current_request.endpoint = TCP_socket.remote_endpoint().address(); current_request.endpoint = TCP_socket.remote_endpoint().address();
request_handler.handle_request(current_request, current_reply); request_handler.HandleRequest(current_request, current_reply);
// compress the result w/ gzip/deflate if requested // compress the result w/ gzip/deflate if requested
switch (compression_type) switch (compression_type)

View File

@ -10,9 +10,9 @@ namespace http
{ {
const char ok_html[] = ""; const char ok_html[] = "";
const char bad_request_html[] = "{\"status\": 400,\"status_message\":\"Bad Request\"}"; const char bad_request_html[] = "";
const char internal_server_error_html[] = const char internal_server_error_html[] =
"{\"status\": 500,\"status_message\":\"Internal Server Error\"}"; "{\"code\": \"internal-error\",\"message\":\"Internal Server Error\"}";
const char seperators[] = {':', ' '}; const char seperators[] = {':', ' '};
const char crlf[] = {'\r', '\n'}; const char crlf[] = {'\r', '\n'};
const std::string http_ok_string = "HTTP/1.0 200 OK\r\n"; const std::string http_ok_string = "HTTP/1.0 200 OK\r\n";

View File

@ -1,6 +1,7 @@
#include "server/request_handler.hpp" #include "server/request_handler.hpp"
#include "server/service_handler.hpp"
#include "server/api_grammar.hpp" #include "server/api/url_parser.hpp"
#include "server/http/reply.hpp" #include "server/http/reply.hpp"
#include "server/http/request.hpp" #include "server/http/request.hpp"
@ -9,7 +10,7 @@
#include "util/string_util.hpp" #include "util/string_util.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
#include "engine/route_parameters.hpp" #include "engine/status.hpp"
#include "util/json_container.hpp" #include "util/json_container.hpp"
#include "osrm/osrm.hpp" #include "osrm/osrm.hpp"
@ -28,11 +29,22 @@ namespace osrm
namespace server namespace server
{ {
RequestHandler::RequestHandler() : routing_machine(nullptr) {}
void RequestHandler::handle_request(const http::request &current_request, void RequestHandler::RegisterServiceHandler(std::unique_ptr<ServiceHandler> service_handler_)
{
service_handler = std::move(service_handler_);
}
void RequestHandler::HandleRequest(const http::request &current_request,
http::reply &current_reply) http::reply &current_reply)
{ {
if (!service_handler)
{
current_reply = http::reply::stock_reply(http::reply::internal_server_error);
util::SimpleLogger().Write(logWARNING) << "No service handler registered." << std::endl;
return;
}
util::json::Object json_result; util::json::Object json_result;
// parse command // parse command
@ -67,84 +79,49 @@ void RequestHandler::handle_request(const http::request &current_request,
<< current_request.agent << (0 == current_request.agent.length() ? "- " : " ") << current_request.agent << (0 == current_request.agent.length() ? "- " : " ")
<< request_string; << request_string;
engine::RouteParameters route_parameters;
APIGrammarParser api_parser(&route_parameters);
auto api_iterator = request_string.begin(); auto api_iterator = request_string.begin();
const bool result = auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());;
boost::spirit::qi::parse(api_iterator, request_string.end(), api_parser);
// check if the was an error with the request // check if the was an error with the request
if (result && api_iterator == request_string.end()) if (maybe_parsed_url && api_iterator == request_string.end())
{ {
// parsing done, lets call the right plugin to handle the request const engine::Status status = service_handler->RunQuery(std::move(*maybe_parsed_url), json_result);
BOOST_ASSERT_MSG(routing_machine != nullptr, "pointer not init'ed"); if (status != engine::Status::Ok)
if (!route_parameters.jsonp_parameter.empty())
{ // prepend response with jsonp parameter
const std::string json_p = (route_parameters.jsonp_parameter + "(");
current_reply.content.insert(current_reply.content.end(), json_p.begin(),
json_p.end());
}
const int return_code = routing_machine->RunQuery(route_parameters, json_result);
json_result.values["status"] = return_code;
// 4xx bad request return code
if (return_code / 100 == 4)
{ {
// 4xx bad request return code
current_reply.status = http::reply::bad_request; current_reply.status = http::reply::bad_request;
current_reply.content.clear();
route_parameters.output_format.clear();
} }
else else
{ {
// 2xx valid request BOOST_ASSERT(status == engine::Status::Ok);
BOOST_ASSERT(return_code / 100 == 2);
} }
} }
else else
{ {
const auto position = std::distance(request_string.begin(), api_iterator); const auto position = std::distance(request_string.begin(), api_iterator);
const auto context_begin = request_string.begin() + std::max(position - 3UL, 0UL);
const auto context_end = request_string.begin() + std::min(position + 3UL, request_string.size());
std::string context(context_begin, context_end);
current_reply.status = http::reply::bad_request; current_reply.status = http::reply::bad_request;
json_result.values["status"] = http::reply::bad_request; json_result.values["code"] = "invalid-url";
json_result.values["status_message"] = json_result.values["message"] =
"Query string malformed close to position " + std::to_string(position); "URL string malformed close to position " + std::to_string(position) + ": \"" + context + "\"";
} }
current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*"); current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*");
current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET"); current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET");
current_reply.headers.emplace_back("Access-Control-Allow-Headers", current_reply.headers.emplace_back("Access-Control-Allow-Headers",
"X-Requested-With, Content-Type"); "X-Requested-With, Content-Type");
current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8");
current_reply.headers.emplace_back("Content-Disposition",
"inline; filename=\"response.json\"");
if (route_parameters.service == "tile" && json_result.values.find("pbf") != json_result.values.end()) util::json::render(current_reply.content, json_result);
{
std::copy(json_result.values["pbf"].get<osrm::util::json::Buffer>().value.cbegin(),
json_result.values["pbf"].get<osrm::util::json::Buffer>().value.cend(),
std::back_inserter(current_reply.content));
current_reply.headers.emplace_back("Content-Type", "application/x-protobuf"); // set headers
}
else if (route_parameters.jsonp_parameter.empty())
{ // json file
util::json::render(current_reply.content, json_result);
current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8");
current_reply.headers.emplace_back("Content-Disposition",
"inline; filename=\"response.json\"");
}
else
{ // jsonp
util::json::render(current_reply.content, json_result);
current_reply.headers.emplace_back("Content-Type", "text/javascript; charset=UTF-8");
current_reply.headers.emplace_back("Content-Disposition",
"inline; filename=\"response.js\"");
}
current_reply.headers.emplace_back("Content-Length", current_reply.headers.emplace_back("Content-Length",
std::to_string(current_reply.content.size())); std::to_string(current_reply.content.size()));
if (!route_parameters.jsonp_parameter.empty())
{ // append brace to jsonp response
current_reply.content.push_back(')');
}
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -154,6 +131,5 @@ void RequestHandler::handle_request(const http::request &current_request,
} }
} }
void RequestHandler::RegisterRoutingMachine(OSRM *osrm) { routing_machine = osrm; }
} }
} }

View File

@ -0,0 +1,83 @@
#include "server/service/route_service.hpp"
#include "engine/api/route_parameters.hpp"
#include "server/api/route_parameters_parser.hpp"
#include "util/json_container.hpp"
#include <boost/format.hpp>
namespace osrm
{
namespace server
{
namespace service
{
namespace {
const constexpr char PARAMETER_SIZE_MISMATCH_MSG[] = "Number of elements in %1% size %2% does not match coordinate size %3%";
template<typename ParamT>
bool constrainParamSize(const char* msg_template, const char* name, const ParamT& param, const std::size_t target_size, std::string& help)
{
if (param.size() > 0 && param.size() != target_size)
{
help = (boost::format(msg_template) % name % param.size() % target_size).str();
return true;
}
return false;
}
std::string getWrongOptionHelp(const engine::api::RouteParameters& parameters)
{
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)
|| constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "uturns", parameters.uturns, 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 RouteService::RunQuery(std::vector<util::FixedPointCoordinate> coordinates,
std::string &options,
util::json::Object &json_result)
{
auto options_iterator = options.begin();
auto parameters = api::parseRouteParameters(options_iterator, options.end());
if (!parameters || options_iterator != options.end())
{
const auto position = std::distance(options.begin(), options_iterator);
json_result.values["code"] = "invalid-options";
json_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())
{
json_result.values["code"] = "invalid-options";
json_result.values["message"] = getWrongOptionHelp(*parameters);
return engine::Status::Error;
}
BOOST_ASSERT(parameters->IsValid());
return BaseService::routing_machine.Route(*parameters, json_result);
}
}
}
}

View File

@ -0,0 +1,39 @@
#include "server/service_handler.hpp"
#include "server/service/route_service.hpp"
#include "server/api/parsed_url.hpp"
#include "util/json_util.hpp"
#include "util/make_unique.hpp"
namespace osrm
{
namespace server
{
ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(config)
{
service_map["route"] = util::make_unique<service::RouteService>(routing_machine);
}
engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, util::json::Object &json_result)
{
const auto& service_iter = service_map.find(parsed_url.service);
if (service_iter == service_map.end())
{
json_result.values["code"] = "invalid-service";
json_result.values["message"] = "Service " + parsed_url.service + " not found!";
return engine::Status::Error;
}
auto &service = service_iter->second;
if (service->GetVersion() != parsed_url.version)
{
json_result.values["code"] = "invalid-version";
json_result.values["message"] = "Service " + parsed_url.service + " not found!";
return engine::Status::Error;
}
return service->RunQuery(std::move(parsed_url.coordinates), parsed_url.options, json_result);
}
}
}

View File

@ -39,7 +39,7 @@ namespace storage
{ {
using RTreeLeaf = using RTreeLeaf =
typename engine::datafacade::BaseDataFacade<contractor::QueryEdge::EdgeData>::RTreeLeaf; typename engine::datafacade::BaseDataFacade::RTreeLeaf;
using RTreeNode = util::StaticRTree<RTreeLeaf, using RTreeNode = util::StaticRTree<RTreeLeaf,
util::ShM<util::FixedPointCoordinate, true>::vector, util::ShM<util::FixedPointCoordinate, true>::vector,
true>::TreeNode; true>::TreeNode;

View File

@ -1,5 +1,6 @@
#include "server/server.hpp" #include "server/server.hpp"
#include "util/routed_options.hpp" #include "util/routed_options.hpp"
#include "util/make_unique.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "osrm/osrm.hpp" #include "osrm/osrm.hpp"
@ -101,10 +102,10 @@ int main(int argc, const char *argv[]) try
pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask); pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
#endif #endif
OSRM osrm_lib(config);
auto routing_server = server::Server::CreateServer(ip_address, ip_port, requested_thread_num); auto routing_server = server::Server::CreateServer(ip_address, ip_port, requested_thread_num);
auto service_handler = util::make_unique<server::ServiceHandler>(config);
routing_server->GetRequestHandlerPtr().RegisterRoutingMachine(&osrm_lib); routing_server->RegisterServiceHandler(std::move(service_handler));
if (trial_run) if (trial_run)
{ {

View File

@ -45,12 +45,17 @@ bool FixedPointCoordinate::IsValid() const
lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION); lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION);
} }
bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs)
{ {
return lat == other.lat && lon == other.lon; return lhs.lat == rhs.lat && lhs.lon == rhs.lon;
} }
std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate) bool operator!=(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs)
{
return !(lhs == rhs);
}
std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate)
{ {
out << "(" << static_cast<double>(coordinate.lat / COORDINATE_PRECISION) << "," out << "(" << static_cast<double>(coordinate.lat / COORDINATE_PRECISION) << ","
<< static_cast<double>(coordinate.lon / COORDINATE_PRECISION) << ")"; << static_cast<double>(coordinate.lon / COORDINATE_PRECISION) << ")";

View File

@ -1,5 +1,4 @@
#include "engine/douglas_peucker.hpp" #include "engine/douglas_peucker.hpp"
#include "engine/segment_information.hpp"
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp> #include <boost/test/test_case_template.hpp>
@ -13,13 +12,7 @@ BOOST_AUTO_TEST_SUITE(douglas_peucker_simplification)
using namespace osrm; using namespace osrm;
using namespace osrm::engine; using namespace osrm::engine;
SegmentInformation getTestInfo(int lat, int lon, bool necessary) BOOST_AUTO_TEST_CASE(removed_middle_test)
{
return SegmentInformation(util::FixedPointCoordinate(lat, lon), 0, 0, 0,
extractor::TurnInstruction::HeadOn, necessary, false, 0);
}
BOOST_AUTO_TEST_CASE(all_necessary_test)
{ {
/* /*
x x
@ -27,19 +20,23 @@ BOOST_AUTO_TEST_CASE(all_necessary_test)
x \ x \
/ \ / \
x x x x
/
*/ */
std::vector<SegmentInformation> info = {getTestInfo(5, 5, true), std::vector<util::FixedPointCoordinate> coordinates = {
getTestInfo(6, 6, true), util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION),
getTestInfo(10, 10, true), util::FixedPointCoordinate(6 * COORDINATE_PRECISION, 6 * COORDINATE_PRECISION),
getTestInfo(5, 15, true)}; util::FixedPointCoordinate(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION),
for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++) util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION)};
// FIXME this test fails for everything below z4 because the DP algorithms
// only used a naive distance measurement
//for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
for (unsigned z = 0; z < 2; z++)
{ {
douglasPeucker(info, z); auto result = douglasPeucker(coordinates, z);
for (const auto &i : info) BOOST_CHECK_EQUAL(result.size(), 3);
{ BOOST_CHECK_EQUAL(result[0], coordinates[0]);
BOOST_CHECK_EQUAL(i.necessary, true); BOOST_CHECK_EQUAL(result[1], coordinates[2]);
} BOOST_CHECK_EQUAL(result[2], coordinates[3]);
} }
} }
@ -54,26 +51,27 @@ BOOST_AUTO_TEST_CASE(remove_second_node_test)
| |
x x
*/ */
std::vector<SegmentInformation> info = { std::vector<util::FixedPointCoordinate> input = {
getTestInfo(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION, true), util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION),
getTestInfo(5 * COORDINATE_PRECISION, util::FixedPointCoordinate(5 * COORDINATE_PRECISION,
5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], false), 5 * COORDINATE_PRECISION +
getTestInfo(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION, false), detail::DOUGLAS_PEUCKER_THRESHOLDS[z]),
getTestInfo(10 * COORDINATE_PRECISION, util::FixedPointCoordinate(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION),
10 + COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2, util::FixedPointCoordinate(10 * COORDINATE_PRECISION,
false), 10 + COORDINATE_PRECISION +
getTestInfo(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION, false), detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2),
getTestInfo(5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION),
15 * COORDINATE_PRECISION, true), util::FixedPointCoordinate(5 * COORDINATE_PRECISION +
detail::DOUGLAS_PEUCKER_THRESHOLDS[z],
15 * COORDINATE_PRECISION),
}; };
BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]); BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]);
douglasPeucker(info, z); auto result = douglasPeucker(input, z);
BOOST_CHECK_EQUAL(info[0].necessary, true); BOOST_CHECK_EQUAL(result.size(), 4);
BOOST_CHECK_EQUAL(info[1].necessary, false); BOOST_CHECK_EQUAL(input[0], result[0]);
BOOST_CHECK_EQUAL(info[2].necessary, true); BOOST_CHECK_EQUAL(input[2], result[1]);
BOOST_CHECK_EQUAL(info[3].necessary, true); BOOST_CHECK_EQUAL(input[3], result[2]);
BOOST_CHECK_EQUAL(info[4].necessary, false); BOOST_CHECK_EQUAL(input[5], result[3]);
BOOST_CHECK_EQUAL(info[5].necessary, true);
} }
} }

View File

@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(decode)
{ {
// Polyline string for the 5 coordinates // Polyline string for the 5 coordinates
const std::string polyline = "_gjaR_gjaR_pR_ibE_pR_ibE_pR_ibE_pR_ibE"; const std::string polyline = "_gjaR_gjaR_pR_ibE_pR_ibE_pR_ibE_pR_ibE";
const auto coords = polylineDecode(polyline); const auto coords = decodePolyline(polyline);
// Test coordinates; these would be the coordinates we give the loc parameter, // Test coordinates; these would be the coordinates we give the loc parameter,
// e.g. loc=10.00,10.0&loc=10.01,10.1... // e.g. loc=10.00,10.0&loc=10.01,10.1...

View File

@ -0,0 +1,168 @@
#include "server/api/route_parameters_parser.hpp"
#include <fstream>
namespace osrm
{
namespace engine
{
namespace api
{
std::ostream &operator<<(std::ostream &out, api::RouteParameters::GeometriesType geometries)
{
switch (geometries)
{
case api::RouteParameters::GeometriesType::GeoJSON:
out << "GeoJSON";
break;
case api::RouteParameters::GeometriesType::Polyline:
out << "Polyline";
break;
default:
BOOST_ASSERT_MSG(false, "GeometriesType not fully captured");
}
return out;
}
std::ostream &operator<<(std::ostream &out, api::RouteParameters::OverviewType overview)
{
switch (overview)
{
case api::RouteParameters::OverviewType::False:
out << "False";
break;
case api::RouteParameters::OverviewType::Full:
out << "Full";
break;
case api::RouteParameters::OverviewType::Simplified:
out << "Simplified";
break;
default:
BOOST_ASSERT_MSG(false, "OverviewType not fully captured");
}
return out;
}
std::ostream &operator<<(std::ostream &out, api::RouteParameters::Bearing bearing)
{
out << bearing.bearing << "," << bearing.range;
return out;
}
}
}
}
#include <boost/test/unit_test.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/optional/optional_io.hpp>
#define CHECK_EQUAL_RANGE(R1, R2) \
BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end());
BOOST_AUTO_TEST_SUITE(api_route_parameters_parser)
using namespace osrm;
using namespace osrm::server;
// returns distance to front
std::size_t testInvalidOptions(std::string options)
{
auto iter = options.begin();
auto result = api::parseRouteParameters(iter, options.end());
BOOST_CHECK(!result);
return std::distance(options.begin(), iter);
}
BOOST_AUTO_TEST_CASE(invalid_urls)
{
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&bla=foo"), 14UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&bearings=foo"), 24UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&uturns=foo"), 22UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&radiuses=foo"), 24UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&hints=foo"), 14UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&geometries=foo"), 14UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&overview=foo"), 14UL);
BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&alternative=foo"), 14UL);
}
BOOST_AUTO_TEST_CASE(valid_urls)
{
engine::api::RouteParameters reference_1{};
auto result_1 = api::parseRouteParameters("");
BOOST_CHECK(result_1);
BOOST_CHECK_EQUAL(reference_1.steps, result_1->steps);
BOOST_CHECK_EQUAL(reference_1.alternative, result_1->alternative);
BOOST_CHECK_EQUAL(reference_1.geometries, result_1->geometries);
BOOST_CHECK_EQUAL(reference_1.overview, result_1->overview);
CHECK_EQUAL_RANGE(reference_1.uturns, result_1->uturns);
CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses);
CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
// bool steps = true;
// bool alternative = true;
// GeometriesType geometries = GeometriesType::Polyline;
// OverviewType overview = OverviewType::False;
// std::vector<boost::optional<bool>> uturns;
engine::api::RouteParameters reference_2{};
auto result_2 = api::parseRouteParameters(
"steps=true&alternative=true&geometries=polyline&overview=simplified");
BOOST_CHECK(result_2);
BOOST_CHECK_EQUAL(reference_2.steps, result_2->steps);
BOOST_CHECK_EQUAL(reference_2.alternative, result_2->alternative);
BOOST_CHECK_EQUAL(reference_2.geometries, result_2->geometries);
BOOST_CHECK_EQUAL(reference_2.overview, result_2->overview);
CHECK_EQUAL_RANGE(reference_2.uturns, result_2->uturns);
CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses);
CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
std::vector<boost::optional<bool>> uturns_3 = {true, false, boost::none};
engine::api::RouteParameters reference_3{
false, false, engine::api::RouteParameters::GeometriesType::GeoJSON,
engine::api::RouteParameters::OverviewType::False, uturns_3};
auto result_3 = api::parseRouteParameters(
"steps=false&alternative=false&geometries=geojson&overview=false&uturns=true;false;");
BOOST_CHECK(result_3);
BOOST_CHECK_EQUAL(reference_3.steps, result_3->steps);
BOOST_CHECK_EQUAL(reference_3.alternative, result_3->alternative);
BOOST_CHECK_EQUAL(reference_3.geometries, result_3->geometries);
BOOST_CHECK_EQUAL(reference_3.overview, result_3->overview);
CHECK_EQUAL_RANGE(reference_3.uturns, result_3->uturns);
CHECK_EQUAL_RANGE(reference_3.bearings, result_3->bearings);
CHECK_EQUAL_RANGE(reference_3.radiuses, result_3->radiuses);
CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates);
std::vector<boost::optional<engine::Hint>> hints_4 = {
engine::Hint::FromBase64(
"rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_0vMAAEAAQGLSzmR"),
engine::Hint::FromBase64(
"_4ghA4JuzAD_IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR"),
engine::Hint::FromBase64(
"03AhA0vnzAA_SAAA_____3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR")};
engine::api::RouteParameters reference_4{false,
true,
engine::api::RouteParameters::GeometriesType::Polyline,
engine::api::RouteParameters::OverviewType::Simplified,
std::vector<boost::optional<bool>>{},
std::vector<util::FixedPointCoordinate>{},
hints_4,
std::vector<boost::optional<double>>{},
std::vector<boost::optional<engine::api::BaseParameters::Bearing>>{}
};
auto result_4 = api::parseRouteParameters(
"steps=false&hints=rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_"
"0vMAAEAAQGLSzmR;_4ghA4JuzAD_"
"IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR;03AhA0vnzAA_SAAA_____"
"3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR");
BOOST_CHECK(result_3);
BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps);
BOOST_CHECK_EQUAL(reference_4.alternative, result_4->alternative);
BOOST_CHECK_EQUAL(reference_4.geometries, result_4->geometries);
BOOST_CHECK_EQUAL(reference_4.overview, result_4->overview);
CHECK_EQUAL_RANGE(reference_4.uturns, result_4->uturns);
CHECK_EQUAL_RANGE(reference_4.bearings, result_4->bearings);
CHECK_EQUAL_RANGE(reference_4.radiuses, result_4->radiuses);
CHECK_EQUAL_RANGE(reference_4.coordinates, result_4->coordinates);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,110 @@
#include "server/api/url_parser.hpp"
#include <fstream>
// needed for BOOST_CHECK_EQUAL
namespace osrm
{
namespace server
{
namespace api
{
std::ostream& operator<<(std::ostream& out, const osrm::server::api::ParsedURL& url)
{
out << url.service << ", " << url.version << ", " << url.profile << ", ";
for (auto c : url.coordinates)
{
out << c << " ";
}
out << ", " << url.options;
return out;
}
}
}
}
#include <boost/test/unit_test.hpp>
#include <boost/test/test_tools.hpp>
#define CHECK_EQUAL_RANGE(R1, R2) \
BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end());
BOOST_AUTO_TEST_SUITE(api_url_parser)
using namespace osrm;
using namespace osrm::server;
// returns distance to front
std::size_t testInvalidURL(std::string url)
{
auto iter = url.begin();
auto result = api::parseURL(iter, url.end());
BOOST_CHECK(!result);
return std::distance(url.begin(), iter);
}
BOOST_AUTO_TEST_CASE(invalid_urls)
{
BOOST_CHECK_EQUAL(testInvalidURL("/route/"), 0UL);
BOOST_CHECK_EQUAL(testInvalidURL("/route/bla"), 0UL);
BOOST_CHECK_EQUAL(testInvalidURL("/route/1/1,2;3;4"), 0UL);
BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/pro_file/1,2;3,4"), 0UL);
BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile"), 0UL);
BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile/"), 0UL);
}
BOOST_AUTO_TEST_CASE(valid_urls)
{
std::vector<util::FixedPointCoordinate> coords_1 = {
// lat,lon
util::FixedPointCoordinate(1 * COORDINATE_PRECISION, 0 * COORDINATE_PRECISION),
util::FixedPointCoordinate(3 * COORDINATE_PRECISION, 2 * COORDINATE_PRECISION),
util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 4 * COORDINATE_PRECISION),
};
api::ParsedURL reference_1{"route", 1, "profile", coords_1, "options=value&foo=bar"};
auto result_1 = api::parseURL("/route/v1/profile/0,1;2,3;4,5?options=value&foo=bar");
BOOST_CHECK(result_1);
BOOST_CHECK_EQUAL(reference_1.service, result_1->service);
BOOST_CHECK_EQUAL(reference_1.version, result_1->version);
BOOST_CHECK_EQUAL(reference_1.profile, result_1->profile);
CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
BOOST_CHECK_EQUAL(reference_1.options, result_1->options);
// no options
api::ParsedURL reference_2{"route", 1, "profile", coords_1, ""};
auto result_2 = api::parseURL("/route/v1/profile/0,1;2,3;4,5");
BOOST_CHECK(result_2);
BOOST_CHECK_EQUAL(reference_2.service, result_2->service);
BOOST_CHECK_EQUAL(reference_2.version, result_2->version);
BOOST_CHECK_EQUAL(reference_2.profile, result_2->profile);
CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
BOOST_CHECK_EQUAL(reference_2.options, result_2->options);
// one coordinate
std::vector<util::FixedPointCoordinate> coords_3 = {
// lat,lon
util::FixedPointCoordinate(1 * COORDINATE_PRECISION, 0 * COORDINATE_PRECISION),
};
api::ParsedURL reference_3{"route", 1, "profile", coords_3, ""};
auto result_3 = api::parseURL("/route/v1/profile/0,1");
BOOST_CHECK(result_3);
BOOST_CHECK_EQUAL(reference_3.service, result_3->service);
BOOST_CHECK_EQUAL(reference_3.version, result_3->version);
BOOST_CHECK_EQUAL(reference_3.profile, result_3->profile);
CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates);
BOOST_CHECK_EQUAL(reference_3.options, result_3->options);
// polyline
api::ParsedURL reference_5{"route", 1, "profile", coords_1, ""};
auto result_5 = api::parseURL("/route/v1/profile/polyline(_ibE?_seK_seK_seK_seK)?");
BOOST_CHECK(result_5);
BOOST_CHECK_EQUAL(reference_5.service, result_5->service);
BOOST_CHECK_EQUAL(reference_5.version, result_5->version);
BOOST_CHECK_EQUAL(reference_5.profile, result_5->profile);
CHECK_EQUAL_RANGE(reference_5.coordinates, result_5->coordinates);
BOOST_CHECK_EQUAL(reference_5.options, result_5->options);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,7 @@
#define BOOST_TEST_MODULE server tests
#include <boost/test/unit_test.hpp>
/*
* This file will contain an automatically generated main function.
*/

74
unit_tests/util/tiles.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "util/tiles.hpp"
using namespace osrm::util;
#include <boost/functional/hash.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <iostream>
BOOST_AUTO_TEST_SUITE(tiles_test)
using namespace osrm;
using namespace osrm::util;
BOOST_AUTO_TEST_CASE(point_to_tile_test)
{
tiles::Tile tile_1_reference{2306375680,1409941503,32};
tiles::Tile tile_2_reference{2308259840,1407668224,32};
tiles::Tile tile_3_reference{616562688,2805989376,32};
tiles::Tile tile_4_reference{1417674752,2084569088,32};
tiles::Tile tile_5_reference{616562688,2805989376,32};
tiles::Tile tile_6_reference{712654285,2671662374,32};
auto tile_1 = tiles::pointToTile(13.31817626953125,52.449314140869696);
auto tile_2 = tiles::pointToTile(13.476104736328125,52.56529070208021);
auto tile_3 = tiles::pointToTile(-128.32031249999997,-48.224672649565186);
auto tile_4 = tiles::pointToTile(-61.17187499999999,5.266007882805498);
auto tile_5 = tiles::pointToTile(-128.32031249999997,-48.224672649565186);
auto tile_6 = tiles::pointToTile(-120.266007882805532,-40.17187499999999);
BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x);
BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y);
BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z);
BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x);
BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y);
BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z);
BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x);
BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y);
BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z);
BOOST_CHECK_EQUAL(tile_4.x, tile_4_reference.x);
BOOST_CHECK_EQUAL(tile_4.y, tile_4_reference.y);
BOOST_CHECK_EQUAL(tile_4.z, tile_4_reference.z);
BOOST_CHECK_EQUAL(tile_5.x, tile_5_reference.x);
BOOST_CHECK_EQUAL(tile_5.y, tile_5_reference.y);
BOOST_CHECK_EQUAL(tile_5.z, tile_5_reference.z);
BOOST_CHECK_EQUAL(tile_6.x, tile_6_reference.x);
BOOST_CHECK_EQUAL(tile_6.y, tile_6_reference.y);
BOOST_CHECK_EQUAL(tile_6.z, tile_6_reference.z);
}
// Verify that the bearing-bounds checking function behaves as expected
BOOST_AUTO_TEST_CASE(bounding_box_to_tile_test)
{
tiles::Tile tile_1_reference{17, 10, 5};
tiles::Tile tile_2_reference{0, 0, 0};
tiles::Tile tile_3_reference{0, 2, 2};
auto tile_1 = tiles::getBBMaxZoomTile(13.31817626953125, 52.449314140869696,
13.476104736328125, 52.56529070208021);
auto tile_2 = tiles::getBBMaxZoomTile(-128.32031249999997, -48.224672649565186,
-61.17187499999999, 5.266007882805498);
auto tile_3 = tiles::getBBMaxZoomTile(-128.32031249999997, -48.224672649565186,
-120.2660078828055, -40.17187499999999);
BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x);
BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y);
BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z);
BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x);
BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y);
BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z);
BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x);
BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y);
BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z);
}
BOOST_AUTO_TEST_SUITE_END()