diff --git a/data_structures/route_parameters.cpp b/data_structures/route_parameters.cpp index 7429e4902..3b615e2c4 100644 --- a/data_structures/route_parameters.cpp +++ b/data_structures/route_parameters.cpp @@ -33,7 +33,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. RouteParameters::RouteParameters() : zoom_level(18), print_instructions(false), alternate_route(true), geometry(true), - compression(true), deprecatedAPI(false), uturn_default(false), check_sum(-1), num_results(1) + compression(true), deprecatedAPI(false), uturn_default(false), classify(false), + matching_beta(-1.0), gps_precision(-1.0), check_sum(-1), num_results(1) { } @@ -83,6 +84,12 @@ void RouteParameters::setInstructionFlag(const bool flag) { print_instructions = 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 ¶meter) diff --git a/include/osrm/route_parameters.hpp b/include/osrm/route_parameters.hpp index 5fe454cee..9babbd763 100644 --- a/include/osrm/route_parameters.hpp +++ b/include/osrm/route_parameters.hpp @@ -49,6 +49,12 @@ struct RouteParameters 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); @@ -80,6 +86,9 @@ struct RouteParameters bool compression; bool deprecatedAPI; bool uturn_default; + bool classify; + double matching_beta; + double gps_precision; unsigned check_sum; short num_results; std::string service; diff --git a/plugins/map_matching.hpp b/plugins/map_matching.hpp index 5bb58e6fc..bacddfc1b 100644 --- a/plugins/map_matching.hpp +++ b/plugins/map_matching.hpp @@ -171,7 +171,8 @@ template class MapMatchingPlugin : public BasePlugin { osrm::json::Object subtrace; - subtrace.values["confidence"] = sub.confidence; + if (route_parameters.classify) + subtrace.values["confidence"] = sub.confidence; if (route_parameters.geometry) { @@ -230,11 +231,18 @@ template class MapMatchingPlugin : public BasePlugin return 400; } - // call the actual map matching + // setup logging if enabled if (osrm::json::Logger::get()) osrm::json::Logger::get()->initialize("matching"); + + // call the actual map matching Matching::SubMatchingList sub_matchings; - search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps, sub_matchings); + search_engine_ptr->map_matching(candidates_lists, + input_coords, + input_timestamps, + route_parameters.matching_beta, + route_parameters.gps_precision, + sub_matchings); if (1 > sub_matchings.size()) { @@ -245,17 +253,20 @@ template class MapMatchingPlugin : public BasePlugin for (auto& sub : sub_matchings) { // classify result - double trace_length = sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()]; - TraceClassification classification = classify(trace_length, - sub.length, - (sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size()); - if (classification.first == ClassifierT::ClassLabel::POSITIVE) + if (route_parameters.classify) { - sub.confidence = classification.second; - } - else - { - sub.confidence = 1-classification.second; + double trace_length = sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()]; + TraceClassification classification = classify(trace_length, + sub.length, + (sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size()); + if (classification.first == ClassifierT::ClassLabel::POSITIVE) + { + sub.confidence = classification.second; + } + else + { + sub.confidence = 1-classification.second; + } } BOOST_ASSERT(sub.nodes.size() > 1); diff --git a/routing_algorithms/map_matching.hpp b/routing_algorithms/map_matching.hpp index 8a751ba1f..1fa881bf2 100644 --- a/routing_algorithms/map_matching.hpp +++ b/routing_algorithms/map_matching.hpp @@ -75,67 +75,42 @@ template class MapMatching final SearchEngineData &engine_working_data; // FIXME this value should be a table based on samples/meter (or samples/min) - constexpr static const double beta = 10.0; - constexpr static const double sigma_z = 4.07; - constexpr static const double log_sigma_z = std::log(sigma_z); + constexpr static const double default_beta = 10.0; + constexpr static const double default_sigma_z = 4.07; constexpr static const double log_2_pi = std::log(2 * M_PI); - constexpr static double emission_probability(const double distance) + // closures to precompute log -> only simple floating point operations + struct EmissionLogProbability { - return (1. / (std::sqrt(2. * M_PI) * sigma_z)) * - std::exp(-0.5 * std::pow((distance / sigma_z), 2.)); - } + double sigma_z; + double log_sigma_z; - constexpr static double transition_probability(const float d_t, const float beta) + EmissionLogProbability(const double sigma_z) + : sigma_z(sigma_z) + , log_sigma_z(std::log(sigma_z)) + { + } + + double operator()(const double distance) const + { + return -0.5 * (log_2_pi + (distance / sigma_z) * (distance / sigma_z)) - log_sigma_z; + } + }; + struct TransitionLogProbability { - return (1. / beta) * std::exp(-d_t / beta); - } + double beta; + double log_beta; + TransitionLogProbability(const double beta) + : beta(beta) + , log_beta(std::log(beta)) + { + } - constexpr static double log_emission_probability(const double distance) - { - return -0.5 * (log_2_pi + (distance / sigma_z) * (distance / sigma_z)) - log_sigma_z; - } - - constexpr static double log_transition_probability(const float d_t, const float beta) - { - return -std::log(beta) - d_t / beta; - } - - // TODO: needs to be estimated from the input locations - // FIXME These values seem wrong. Higher beta for more samples/minute? Should be inverse - // proportional. - // samples/min and beta - // 1 0.49037673 - // 2 0.82918373 - // 3 1.24364564 - // 4 1.67079581 - // 5 2.00719298 - // 6 2.42513007 - // 7 2.81248831 - // 8 3.15745473 - // 9 3.52645392 - // 10 4.09511775 - // 11 4.67319795 - // 21 12.55107715 - // 12 5.41088180 - // 13 6.47666590 - // 14 6.29010734 - // 15 7.80752112 - // 16 8.09074504 - // 17 8.08550528 - // 18 9.09405065 - // 19 11.09090603 - // 20 11.87752824 - // 21 12.55107715 - // 22 15.82820829 - // 23 17.69496773 - // 24 18.07655652 - // 25 19.63438911 - // 26 25.40832185 - // 27 23.76001877 - // 28 28.43289797 - // 29 32.21683062 - // 30 34.56991141 + double operator()(const double d_t) const + { + return -log_beta - d_t / beta; + } + }; double get_network_distance(const PhantomNode &source_phantom, const PhantomNode &target_phantom) const @@ -231,9 +206,10 @@ template class MapMatching final std::vector breakage; const Matching::CandidateLists &candidates_list; + const EmissionLogProbability& emission_log_probability; - HiddenMarkovModel(const Matching::CandidateLists &candidates_list) - : breakage(candidates_list.size()), candidates_list(candidates_list) + HiddenMarkovModel(const Matching::CandidateLists &candidates_list, const EmissionLogProbability& emission_log_probability) + : breakage(candidates_list.size()), candidates_list(candidates_list), emission_log_probability(emission_log_probability) { for (const auto &l : candidates_list) { @@ -271,7 +247,7 @@ template class MapMatching final for (auto s = 0u; s < viterbi[initial_timestamp].size(); ++s) { viterbi[initial_timestamp][s] = - log_emission_probability(candidates_list[initial_timestamp][s].second); + emission_log_probability(candidates_list[initial_timestamp][s].second); parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s); pruned[initial_timestamp][s] = viterbi[initial_timestamp][s] < Matching::MINIMAL_LOG_PROB; @@ -297,6 +273,7 @@ template class MapMatching final } }; + // Provides the debug interface for introspection tools struct DebugInfo { DebugInfo(const osrm::json::Logger* logger) @@ -419,11 +396,17 @@ template class MapMatching final void operator()(const Matching::CandidateLists &candidates_list, const std::vector &trace_coordinates, const std::vector &trace_timestamps, + const double matching_beta, + const double gps_precision, Matching::SubMatchingList &sub_matchings) const { BOOST_ASSERT(candidates_list.size() > 0); - HiddenMarkovModel model(candidates_list); + // TODO replace default values with table lookup based on sampling frequency + EmissionLogProbability emission_log_probability(gps_precision > 0 ? gps_precision : default_sigma_z); + TransitionLogProbability transition_log_probability(matching_beta > 0 ? matching_beta : default_beta); + + HiddenMarkovModel model(candidates_list, emission_log_probability); unsigned initial_timestamp = model.initialize(0); if (initial_timestamp == Matching::INVALID_STATE) @@ -464,7 +447,7 @@ template class MapMatching final { // how likely is candidate s_prime at time t to be emitted? const double emission_pr = - log_emission_probability(candidates_list[t][s_prime].second); + emission_log_probability(candidates_list[t][s_prime].second); double new_value = prev_viterbi[s] + emission_pr; if (current_viterbi[s_prime] > new_value) continue; @@ -483,7 +466,7 @@ template class MapMatching final if (d_t > 500) continue; - const double transition_pr = log_transition_probability(d_t, beta); + const double transition_pr = transition_log_probability(d_t); new_value += transition_pr; debug.add_transition_info(prev_unbroken_timestamp, t, s, s_prime, diff --git a/server/api_grammar.hpp b/server/api_grammar.hpp index 3d7f4ea4b..5c7dcde45 100644 --- a/server/api_grammar.hpp +++ b/server/api_grammar.hpp @@ -41,7 +41,8 @@ template struct APIGrammar : qi::grammar> string[boost::bind(&HandlerT::setService, handler, ::_1)] >> *(query) >> -(uturns); query = ('?') >> (+(zoom | output | jsonp | checksum | location | hint | timestamp | u | cmp | - language | instruction | geometry | alt_route | old_API | num_results)); + language | instruction | geometry | alt_route | old_API | num_results | + matching_beta | gps_precision | classify)); zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> qi::short_[boost::bind(&HandlerT::setZoomLevel, handler, ::_1)]; @@ -64,6 +65,12 @@ template struct APIGrammar : qi::grammar> qi::lit("t") >> '=' >> qi::uint_[boost::bind(&HandlerT::addTimestamp, handler, ::_1)]; + matching_beta = (-qi::lit('&')) >> qi::lit("matching_beta") >> '=' >> + qi::short_[boost::bind(&HandlerT::setMatchingBeta, handler, ::_1)]; + gps_precision = (-qi::lit('&')) >> qi::lit("gps_precision") >> '=' >> + qi::short_[boost::bind(&HandlerT::setGPSPrecision, handler, ::_1)]; + classify = (-qi::lit('&')) >> qi::lit("classify") >> '=' >> + qi::bool_[boost::bind(&HandlerT::setClassify, handler, ::_1)]; u = (-qi::lit('&')) >> qi::lit("u") >> '=' >> qi::bool_[boost::bind(&HandlerT::setUTurn, handler, ::_1)]; uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >> @@ -85,7 +92,7 @@ template struct APIGrammar : qi::grammar api_call, query; qi::rule service, zoom, output, string, jsonp, checksum, location, - hint, timestamp, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u, + hint, timestamp, matching_beta, gps_precision, classify, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u, uturns, old_API, num_results; HandlerT *handler;