Add timestamp parameters and reset to beginning of breakage

This commit is contained in:
Patrick Niklaus 2015-02-20 15:21:50 +01:00
parent d429485f0c
commit 70703c39f3
5 changed files with 76 additions and 31 deletions

View File

@ -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)
{
language = language_string;

View File

@ -63,6 +63,8 @@ struct RouteParameters
void addHint(const std::string &hint);
void addTimestamp(const unsigned timestamp);
void setLanguage(const std::string &language);
void setGeometryFlag(const bool flag);
@ -85,6 +87,7 @@ struct RouteParameters
std::string jsonp_parameter;
std::string language;
std::vector<std::string> hints;
std::vector<unsigned> timestamps;
std::vector<bool> uturns;
std::vector<FixedPointCoordinate> coordinates;
};

View File

@ -172,6 +172,11 @@ template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
std::vector<double> sub_trace_lengths;
Matching::CandidateLists candidates_lists;
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);
if (!found_candidates)
{
@ -181,7 +186,7 @@ template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
// call the actual map matching
JSON::Object debug_info;
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())
{

View File

@ -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 MINIMAL_LOG_PROB = -std::numeric_limits<double>::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_TIME = 180;
}
// 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<bool> breakage;
const Matching::CandidateLists& timestamp_list;
const Matching::CandidateLists& candidates_list;
HiddenMarkovModel(const Matching::CandidateLists& timestamp_list)
: breakage(timestamp_list.size())
, timestamp_list(timestamp_list)
HiddenMarkovModel(const Matching::CandidateLists& candidates_list)
: breakage(candidates_list.size())
, candidates_list(candidates_list)
{
for (const auto& l : timestamp_list)
for (const auto& l : candidates_list)
{
viterbi.emplace_back(l.size());
parents.emplace_back(l.size());
@ -289,13 +289,13 @@ template <class DataFacadeT> class MapMatching final
unsigned initialize(unsigned initial_timestamp)
{
BOOST_ASSERT(initial_timestamp < timestamp_list.size());
BOOST_ASSERT(initial_timestamp < candidates_list.size());
do
{
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);
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 &timestamp_list,
const std::vector<FixedPointCoordinate> coordinate_list,
void operator()(const Matching::CandidateLists &candidates_list,
const std::vector<FixedPointCoordinate>& trace_coordinates,
const std::vector<unsigned>& trace_timestamps,
Matching::SubMatchingList& sub_matchings,
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);
if (initial_timestamp == Matching::INVALID_STATE)
@ -344,15 +345,15 @@ template <class DataFacadeT> class MapMatching final
}
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;
for (unsigned s = 0; s < timestamp_list[t].size(); s++)
for (unsigned s = 0; s < candidates_list[t].size(); s++)
{
JSON::Object _debug_state;
_debug_state.values["transitions"] = JSON::Array();
_debug_state.values["coordinate"] = makeJSONArray(timestamp_list[t][s].first.location.lat / COORDINATE_PRECISION,
timestamp_list[t][s].first.location.lon / COORDINATE_PRECISION);
_debug_state.values["coordinate"] = makeJSONArray(candidates_list[t][s].first.location.lat / COORDINATE_PRECISION,
candidates_list[t][s].first.location.lon / COORDINATE_PRECISION);
if (t < initial_timestamp)
{
_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);
}
unsigned breakage_begin = std::numeric_limits<unsigned>::max();
std::vector<unsigned> split_points;
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);
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();
const auto& prev_viterbi = model.viterbi[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_coordinate = coordinate_list[prev_unbroken_timestamp];
const auto& prev_unbroken_timestamps_list = candidates_list[prev_unbroken_timestamp];
const auto& prev_coordinate = trace_coordinates[prev_unbroken_timestamp];
auto& current_viterbi = model.viterbi[t];
auto& current_pruned = model.pruned[t];
auto& current_parents = model.parents[t];
auto& current_lengths = model.path_lengths[t];
const auto& current_timestamps_list = timestamp_list[t];
const auto& current_coordinate = coordinate_list[t];
const auto& current_timestamps_list = candidates_list[t];
const auto& current_coordinate = trace_coordinates[t];
// compute d_t for this timestamp and the next one
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)
{
// 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;
if (current_viterbi[s_prime] > new_value)
continue;
@ -456,23 +458,47 @@ template <class DataFacadeT> class MapMatching final
{
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
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
// -> 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
model.clear(t);
unsigned new_start = model.initialize(t);
model.clear(breakage_begin);
unsigned new_start = model.initialize(breakage_begin);
// no new start was found -> stop viterbi calculation
if (new_start == Matching::INVALID_STATE)
{
break;
}
prev_unbroken_timestamps.clear();
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
@ -540,7 +566,7 @@ template <class DataFacadeT> class MapMatching final
auto location_index = reconstructed_indices[i].second;
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];
_debug_states.values[timestamp_index]

View File

@ -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)] >>
*(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));
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)];
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)];
u = (-qi::lit('&')) >> qi::lit("u") >> '=' >>
qi::bool_[boost::bind(&HandlerT::setUTurn, handler, ::_1)];
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, 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;
HandlerT *handler;