Add timestamp parameters and reset to beginning of breakage
This commit is contained in:
parent
d429485f0c
commit
70703c39f3
@ -99,6 +99,15 @@ void RouteParameters::addHint(const std::string &hint)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RouteParameters::addTimestamp(const unsigned timestamp)
|
||||||
|
{
|
||||||
|
timestamps.resize(coordinates.size());
|
||||||
|
if (!timestamps.empty())
|
||||||
|
{
|
||||||
|
timestamps.back() = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RouteParameters::setLanguage(const std::string &language_string)
|
void RouteParameters::setLanguage(const std::string &language_string)
|
||||||
{
|
{
|
||||||
language = language_string;
|
language = language_string;
|
||||||
|
@ -63,6 +63,8 @@ struct RouteParameters
|
|||||||
|
|
||||||
void addHint(const std::string &hint);
|
void addHint(const std::string &hint);
|
||||||
|
|
||||||
|
void addTimestamp(const unsigned timestamp);
|
||||||
|
|
||||||
void setLanguage(const std::string &language);
|
void setLanguage(const std::string &language);
|
||||||
|
|
||||||
void setGeometryFlag(const bool flag);
|
void setGeometryFlag(const bool flag);
|
||||||
@ -85,6 +87,7 @@ struct RouteParameters
|
|||||||
std::string jsonp_parameter;
|
std::string jsonp_parameter;
|
||||||
std::string language;
|
std::string language;
|
||||||
std::vector<std::string> hints;
|
std::vector<std::string> hints;
|
||||||
|
std::vector<unsigned> timestamps;
|
||||||
std::vector<bool> uturns;
|
std::vector<bool> uturns;
|
||||||
std::vector<FixedPointCoordinate> coordinates;
|
std::vector<FixedPointCoordinate> coordinates;
|
||||||
};
|
};
|
||||||
|
@ -172,6 +172,11 @@ template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
|
|||||||
std::vector<double> sub_trace_lengths;
|
std::vector<double> sub_trace_lengths;
|
||||||
Matching::CandidateLists candidates_lists;
|
Matching::CandidateLists candidates_lists;
|
||||||
const auto& input_coords = route_parameters.coordinates;
|
const auto& input_coords = route_parameters.coordinates;
|
||||||
|
const auto& input_timestamps = route_parameters.timestamps;
|
||||||
|
if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size())
|
||||||
|
{
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
bool found_candidates = get_candiates(input_coords, sub_trace_lengths, candidates_lists);
|
bool found_candidates = get_candiates(input_coords, sub_trace_lengths, candidates_lists);
|
||||||
if (!found_candidates)
|
if (!found_candidates)
|
||||||
{
|
{
|
||||||
@ -181,7 +186,7 @@ template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
|
|||||||
// call the actual map matching
|
// call the actual map matching
|
||||||
JSON::Object debug_info;
|
JSON::Object debug_info;
|
||||||
Matching::SubMatchingList sub_matchings;
|
Matching::SubMatchingList sub_matchings;
|
||||||
search_engine_ptr->map_matching(candidates_lists, input_coords, sub_matchings, debug_info);
|
search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps, sub_matchings, debug_info);
|
||||||
|
|
||||||
if (1 > sub_matchings.size())
|
if (1 > sub_matchings.size())
|
||||||
{
|
{
|
||||||
|
@ -86,8 +86,8 @@ constexpr static const unsigned max_number_of_candidates = 10;
|
|||||||
constexpr static const double IMPOSSIBLE_LOG_PROB = -std::numeric_limits<double>::infinity();
|
constexpr static const double IMPOSSIBLE_LOG_PROB = -std::numeric_limits<double>::infinity();
|
||||||
constexpr static const double MINIMAL_LOG_PROB = -std::numeric_limits<double>::max();
|
constexpr static const double MINIMAL_LOG_PROB = -std::numeric_limits<double>::max();
|
||||||
constexpr static const unsigned INVALID_STATE = std::numeric_limits<unsigned>::max();
|
constexpr static const unsigned INVALID_STATE = std::numeric_limits<unsigned>::max();
|
||||||
// FIXME that should be a time threshold.
|
|
||||||
constexpr static const unsigned MAX_BROKEN_STATES = 6;
|
constexpr static const unsigned MAX_BROKEN_STATES = 6;
|
||||||
|
constexpr static const unsigned MAX_BROKEN_TIME = 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
// implements a hidden markov model map matching algorithm
|
// implements a hidden markov model map matching algorithm
|
||||||
@ -252,14 +252,14 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
std::vector<std::vector<bool>> pruned;
|
std::vector<std::vector<bool>> pruned;
|
||||||
std::vector<bool> breakage;
|
std::vector<bool> breakage;
|
||||||
|
|
||||||
const Matching::CandidateLists& timestamp_list;
|
const Matching::CandidateLists& candidates_list;
|
||||||
|
|
||||||
|
|
||||||
HiddenMarkovModel(const Matching::CandidateLists& timestamp_list)
|
HiddenMarkovModel(const Matching::CandidateLists& candidates_list)
|
||||||
: breakage(timestamp_list.size())
|
: breakage(candidates_list.size())
|
||||||
, timestamp_list(timestamp_list)
|
, candidates_list(candidates_list)
|
||||||
{
|
{
|
||||||
for (const auto& l : timestamp_list)
|
for (const auto& l : candidates_list)
|
||||||
{
|
{
|
||||||
viterbi.emplace_back(l.size());
|
viterbi.emplace_back(l.size());
|
||||||
parents.emplace_back(l.size());
|
parents.emplace_back(l.size());
|
||||||
@ -289,13 +289,13 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
|
|
||||||
unsigned initialize(unsigned initial_timestamp)
|
unsigned initialize(unsigned initial_timestamp)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(initial_timestamp < timestamp_list.size());
|
BOOST_ASSERT(initial_timestamp < candidates_list.size());
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
for (auto s = 0u; s < viterbi[initial_timestamp].size(); ++s)
|
for (auto s = 0u; s < viterbi[initial_timestamp].size(); ++s)
|
||||||
{
|
{
|
||||||
viterbi[initial_timestamp][s] = log_emission_probability(timestamp_list[initial_timestamp][s].second);
|
viterbi[initial_timestamp][s] = log_emission_probability(candidates_list[initial_timestamp][s].second);
|
||||||
parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s);
|
parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s);
|
||||||
pruned[initial_timestamp][s] = viterbi[initial_timestamp][s] < Matching::MINIMAL_LOG_PROB;
|
pruned[initial_timestamp][s] = viterbi[initial_timestamp][s] < Matching::MINIMAL_LOG_PROB;
|
||||||
|
|
||||||
@ -328,14 +328,15 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void operator()(const Matching::CandidateLists ×tamp_list,
|
void operator()(const Matching::CandidateLists &candidates_list,
|
||||||
const std::vector<FixedPointCoordinate> coordinate_list,
|
const std::vector<FixedPointCoordinate>& trace_coordinates,
|
||||||
|
const std::vector<unsigned>& trace_timestamps,
|
||||||
Matching::SubMatchingList& sub_matchings,
|
Matching::SubMatchingList& sub_matchings,
|
||||||
JSON::Object& _debug_info) const
|
JSON::Object& _debug_info) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(timestamp_list.size() > 0);
|
BOOST_ASSERT(candidates_list.size() > 0);
|
||||||
|
|
||||||
HiddenMarkovModel model(timestamp_list);
|
HiddenMarkovModel model(candidates_list);
|
||||||
|
|
||||||
unsigned initial_timestamp = model.initialize(0);
|
unsigned initial_timestamp = model.initialize(0);
|
||||||
if (initial_timestamp == Matching::INVALID_STATE)
|
if (initial_timestamp == Matching::INVALID_STATE)
|
||||||
@ -344,15 +345,15 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSON::Array _debug_states;
|
JSON::Array _debug_states;
|
||||||
for (unsigned t = 0; t < timestamp_list.size(); t++)
|
for (unsigned t = 0; t < candidates_list.size(); t++)
|
||||||
{
|
{
|
||||||
JSON::Array _debug_timestamps;
|
JSON::Array _debug_timestamps;
|
||||||
for (unsigned s = 0; s < timestamp_list[t].size(); s++)
|
for (unsigned s = 0; s < candidates_list[t].size(); s++)
|
||||||
{
|
{
|
||||||
JSON::Object _debug_state;
|
JSON::Object _debug_state;
|
||||||
_debug_state.values["transitions"] = JSON::Array();
|
_debug_state.values["transitions"] = JSON::Array();
|
||||||
_debug_state.values["coordinate"] = makeJSONArray(timestamp_list[t][s].first.location.lat / COORDINATE_PRECISION,
|
_debug_state.values["coordinate"] = makeJSONArray(candidates_list[t][s].first.location.lat / COORDINATE_PRECISION,
|
||||||
timestamp_list[t][s].first.location.lon / COORDINATE_PRECISION);
|
candidates_list[t][s].first.location.lon / COORDINATE_PRECISION);
|
||||||
if (t < initial_timestamp)
|
if (t < initial_timestamp)
|
||||||
{
|
{
|
||||||
_debug_state.values["viterbi"] = makeJSONSafe(Matching::IMPOSSIBLE_LOG_PROB);
|
_debug_state.values["viterbi"] = makeJSONSafe(Matching::IMPOSSIBLE_LOG_PROB);
|
||||||
@ -368,24 +369,25 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
_debug_states.values.push_back(_debug_timestamps);
|
_debug_states.values.push_back(_debug_timestamps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned breakage_begin = std::numeric_limits<unsigned>::max();
|
||||||
std::vector<unsigned> split_points;
|
std::vector<unsigned> split_points;
|
||||||
std::vector<unsigned> prev_unbroken_timestamps;
|
std::vector<unsigned> prev_unbroken_timestamps;
|
||||||
prev_unbroken_timestamps.reserve(timestamp_list.size());
|
prev_unbroken_timestamps.reserve(candidates_list.size());
|
||||||
prev_unbroken_timestamps.push_back(initial_timestamp);
|
prev_unbroken_timestamps.push_back(initial_timestamp);
|
||||||
for (auto t = initial_timestamp + 1; t < timestamp_list.size(); ++t)
|
for (auto t = initial_timestamp + 1; t < candidates_list.size(); ++t)
|
||||||
{
|
{
|
||||||
unsigned prev_unbroken_timestamp = prev_unbroken_timestamps.back();
|
unsigned prev_unbroken_timestamp = prev_unbroken_timestamps.back();
|
||||||
const auto& prev_viterbi = model.viterbi[prev_unbroken_timestamp];
|
const auto& prev_viterbi = model.viterbi[prev_unbroken_timestamp];
|
||||||
const auto& prev_pruned = model.pruned[prev_unbroken_timestamp];
|
const auto& prev_pruned = model.pruned[prev_unbroken_timestamp];
|
||||||
const auto& prev_unbroken_timestamps_list = timestamp_list[prev_unbroken_timestamp];
|
const auto& prev_unbroken_timestamps_list = candidates_list[prev_unbroken_timestamp];
|
||||||
const auto& prev_coordinate = coordinate_list[prev_unbroken_timestamp];
|
const auto& prev_coordinate = trace_coordinates[prev_unbroken_timestamp];
|
||||||
|
|
||||||
auto& current_viterbi = model.viterbi[t];
|
auto& current_viterbi = model.viterbi[t];
|
||||||
auto& current_pruned = model.pruned[t];
|
auto& current_pruned = model.pruned[t];
|
||||||
auto& current_parents = model.parents[t];
|
auto& current_parents = model.parents[t];
|
||||||
auto& current_lengths = model.path_lengths[t];
|
auto& current_lengths = model.path_lengths[t];
|
||||||
const auto& current_timestamps_list = timestamp_list[t];
|
const auto& current_timestamps_list = candidates_list[t];
|
||||||
const auto& current_coordinate = coordinate_list[t];
|
const auto& current_coordinate = trace_coordinates[t];
|
||||||
|
|
||||||
// compute d_t for this timestamp and the next one
|
// compute d_t for this timestamp and the next one
|
||||||
for (auto s = 0u; s < prev_viterbi.size(); ++s)
|
for (auto s = 0u; s < prev_viterbi.size(); ++s)
|
||||||
@ -396,7 +398,7 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
for (auto s_prime = 0u; s_prime < current_viterbi.size(); ++s_prime)
|
for (auto s_prime = 0u; s_prime < current_viterbi.size(); ++s_prime)
|
||||||
{
|
{
|
||||||
// how likely is candidate s_prime at time t to be emitted?
|
// how likely is candidate s_prime at time t to be emitted?
|
||||||
const double emission_pr = log_emission_probability(timestamp_list[t][s_prime].second);
|
const double emission_pr = log_emission_probability(candidates_list[t][s_prime].second);
|
||||||
double new_value = prev_viterbi[s] + emission_pr;
|
double new_value = prev_viterbi[s] + emission_pr;
|
||||||
if (current_viterbi[s_prime] > new_value)
|
if (current_viterbi[s_prime] > new_value)
|
||||||
continue;
|
continue;
|
||||||
@ -456,23 +458,47 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
{
|
{
|
||||||
BOOST_ASSERT(prev_unbroken_timestamps.size() > 0);
|
BOOST_ASSERT(prev_unbroken_timestamps.size() > 0);
|
||||||
|
|
||||||
|
// save start of breakage -> we need this as split point
|
||||||
|
if (t < breakage_begin)
|
||||||
|
{
|
||||||
|
breakage_begin = t;
|
||||||
|
}
|
||||||
|
|
||||||
// remove both ends of the breakage
|
// remove both ends of the breakage
|
||||||
prev_unbroken_timestamps.pop_back();
|
prev_unbroken_timestamps.pop_back();
|
||||||
|
|
||||||
|
bool trace_split = prev_unbroken_timestamps.size() < 1;
|
||||||
|
|
||||||
|
// use temporal information to determine a split if available
|
||||||
|
if (trace_timestamps.size() > 0)
|
||||||
|
{
|
||||||
|
trace_split = trace_split || (trace_timestamps[t] - trace_timestamps[prev_unbroken_timestamps.back()] > Matching::MAX_BROKEN_TIME);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace_split = trace_split || (t - prev_unbroken_timestamps.back() > Matching::MAX_BROKEN_STATES);
|
||||||
|
}
|
||||||
|
|
||||||
// we reached the beginning of the trace and it is still broken
|
// we reached the beginning of the trace and it is still broken
|
||||||
// -> split the trace here
|
// -> split the trace here
|
||||||
if (prev_unbroken_timestamps.size() < 1 || t - prev_unbroken_timestamps.back() > Matching::MAX_BROKEN_STATES)
|
if (trace_split)
|
||||||
{
|
{
|
||||||
split_points.push_back(t);
|
split_points.push_back(breakage_begin);
|
||||||
// note this preserves everything before t
|
// note this preserves everything before t
|
||||||
model.clear(t);
|
model.clear(breakage_begin);
|
||||||
unsigned new_start = model.initialize(t);
|
unsigned new_start = model.initialize(breakage_begin);
|
||||||
// no new start was found -> stop viterbi calculation
|
// no new start was found -> stop viterbi calculation
|
||||||
if (new_start == Matching::INVALID_STATE)
|
if (new_start == Matching::INVALID_STATE)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
prev_unbroken_timestamps.clear();
|
||||||
prev_unbroken_timestamps.push_back(new_start);
|
prev_unbroken_timestamps.push_back(new_start);
|
||||||
|
// Important: We potentially go back here!
|
||||||
|
// However since t+1 > new_start >= breakge_begin
|
||||||
|
// we can only reset trace_coordindates.size() times.
|
||||||
|
t = new_start;
|
||||||
|
breakage_begin = std::numeric_limits<unsigned>::max();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -540,7 +566,7 @@ template <class DataFacadeT> class MapMatching final
|
|||||||
auto location_index = reconstructed_indices[i].second;
|
auto location_index = reconstructed_indices[i].second;
|
||||||
|
|
||||||
matching.indices[i] = timestamp_index;
|
matching.indices[i] = timestamp_index;
|
||||||
matching.nodes[i] = timestamp_list[timestamp_index][location_index].first;
|
matching.nodes[i] = candidates_list[timestamp_index][location_index].first;
|
||||||
matching.length += model.path_lengths[timestamp_index][location_index];
|
matching.length += model.path_lengths[timestamp_index][location_index];
|
||||||
|
|
||||||
_debug_states.values[timestamp_index]
|
_debug_states.values[timestamp_index]
|
||||||
|
@ -40,7 +40,7 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
|||||||
{
|
{
|
||||||
api_call = qi::lit('/') >> string[boost::bind(&HandlerT::setService, handler, ::_1)] >>
|
api_call = qi::lit('/') >> string[boost::bind(&HandlerT::setService, handler, ::_1)] >>
|
||||||
*(query) >> -(uturns);
|
*(query) >> -(uturns);
|
||||||
query = ('?') >> (+(zoom | output | jsonp | checksum | location | hint | u | cmp |
|
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));
|
||||||
|
|
||||||
zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >>
|
zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >>
|
||||||
@ -62,6 +62,8 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
|||||||
qi::double_)[boost::bind(&HandlerT::addCoordinate, handler, ::_1)];
|
qi::double_)[boost::bind(&HandlerT::addCoordinate, handler, ::_1)];
|
||||||
hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >>
|
hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >>
|
||||||
stringwithDot[boost::bind(&HandlerT::addHint, handler, ::_1)];
|
stringwithDot[boost::bind(&HandlerT::addHint, handler, ::_1)];
|
||||||
|
timestamp = (-qi::lit('&')) >> qi::lit("t") >> '=' >>
|
||||||
|
qi::uint_[boost::bind(&HandlerT::addTimestamp, handler, ::_1)];
|
||||||
u = (-qi::lit('&')) >> qi::lit("u") >> '=' >>
|
u = (-qi::lit('&')) >> qi::lit("u") >> '=' >>
|
||||||
qi::bool_[boost::bind(&HandlerT::setUTurn, handler, ::_1)];
|
qi::bool_[boost::bind(&HandlerT::setUTurn, handler, ::_1)];
|
||||||
uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >>
|
uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >>
|
||||||
@ -83,7 +85,7 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
|||||||
|
|
||||||
qi::rule<Iterator> api_call, query;
|
qi::rule<Iterator> api_call, query;
|
||||||
qi::rule<Iterator, std::string()> service, zoom, output, string, jsonp, checksum, location,
|
qi::rule<Iterator, std::string()> service, zoom, output, string, jsonp, checksum, location,
|
||||||
hint, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u,
|
hint, timestamp, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u,
|
||||||
uturns, old_API, num_results;
|
uturns, old_API, num_results;
|
||||||
|
|
||||||
HandlerT *handler;
|
HandlerT *handler;
|
||||||
|
Loading…
Reference in New Issue
Block a user