Fast map matching  0.1.0
stmatch_algorithm.cpp
1 #include "mm/stmatch/stmatch_algorithm.hpp"
2 #include "algorithm/geom_algorithm.hpp"
3 #include "util/debug.hpp"
4 #include "util/util.hpp"
5 
6 #include <limits>
7 
8 using namespace FMM;
9 using namespace FMM::CORE;
10 using namespace FMM::NETWORK;
11 using namespace FMM::MM;
12 using namespace FMM::PYTHON;
13 
14 STMATCHConfig::STMATCHConfig(
15  int k_arg, double r_arg, double gps_error_arg,
16  double vmax_arg, double factor_arg) :
17  k(k_arg), radius(r_arg), gps_error(gps_error_arg),
18  vmax(vmax_arg), factor(factor_arg) {
19 };
20 
21 void STMATCHConfig::print() const {
22  SPDLOG_INFO("STMATCHAlgorithmConfig");
23  SPDLOG_INFO("k {} radius {} gps_error {} vmax {} factor {}",
25 };
26 
28  const boost::property_tree::ptree &xml_data) {
29  int k = xml_data.get("config.parameters.k", 8);
30  double radius = xml_data.get("config.parameters.r", 300.0);
31  double gps_error = xml_data.get("config.parameters.gps_error", 50.0);
32  double vmax = xml_data.get("config.parameters.vmax", 80.0);;
33  double factor = xml_data.get("config.parameters.factor", 1.5);;
35 };
36 
38  const cxxopts::ParseResult &arg_data) {
39  int k = arg_data["candidates"].as<int>();
40  double radius = arg_data["radius"].as<double>();
41  double gps_error = arg_data["error"].as<double>();
42  double vmax = arg_data["vmax"].as<double>();
43  double factor = arg_data["factor"].as<double>();
45 };
46 
48  if (gps_error <= 0 || radius <= 0 || k <= 0 || vmax <= 0 || factor <= 0) {
49  SPDLOG_CRITICAL("Invalid mm parameter k {} r {} gps error {} vmax {} f {}",
51  return false;
52  }
53  return true;
54 }
55 
57  const std::string &wkt, const STMATCHConfig &config) {
58  LineString line = wkt2linestring(wkt);
59  std::vector<double> timestamps;
60  Trajectory traj{0, line, timestamps};
61  MatchResult result = match_traj(traj, config);
62  PyMatchResult output;
63  output.id = result.id;
64  output.opath = result.opath;
65  output.cpath = result.cpath;
66  output.mgeom = result.mgeom;
67  output.indices = result.indices;
68  for (int i = 0; i < result.opt_candidate_path.size(); ++i) {
69  const MatchedCandidate &mc = result.opt_candidate_path[i];
70  output.candidates.push_back(
71  {i,
72  mc.c.edge->id,
73  graph_.get_node_id(mc.c.edge->source),
74  graph_.get_node_id(mc.c.edge->target),
75  mc.c.dist,
76  mc.c.offset,
77  mc.c.edge->length,
78  mc.ep,
79  mc.tp,
80  mc.sp_dist}
81  );
82  output.pgeom.add_point(mc.c.point);
83  }
84  return output;
85 };
86 
87 // Procedure of HMM based map matching algorithm.
89  const STMATCHConfig &config) {
90  SPDLOG_TRACE("Count of points in trajectory {}", traj.geom.get_num_points());
91  SPDLOG_TRACE("Search candidates");
92  Traj_Candidates tc = network_.search_tr_cs_knn(
93  traj.geom, config.k, config.radius);
94  SPDLOG_TRACE("Trajectory candidate {}", tc);
95  if (tc.empty()) return MatchResult{};
96  SPDLOG_TRACE("Generate dummy graph");
97  DummyGraph dg(tc);
98  SPDLOG_TRACE("Generate composite_graph");
99  CompositeGraph cg(graph_, dg);
100  SPDLOG_TRACE("Generate composite_graph");
101  TransitionGraph tg(tc, config.gps_error);
102  SPDLOG_TRACE("Update cost in transition graph");
103  // The network will be used internally to update transition graph
104  update_tg(&tg, cg, traj, config);
105  SPDLOG_TRACE("Optimal path inference");
106  TGOpath tg_opath = tg.backtrack();
107  SPDLOG_TRACE("Optimal path size {}", tg_opath.size());
108  MatchedCandidatePath matched_candidate_path(tg_opath.size());
109  std::transform(tg_opath.begin(), tg_opath.end(),
110  matched_candidate_path.begin(),
111  [](const TGNode *a) {
112  return MatchedCandidate{
113  *(a->c), a->ep, a->tp, a->sp_dist
114  };
115  });
116  O_Path opath(tg_opath.size());
117  std::transform(tg_opath.begin(), tg_opath.end(),
118  opath.begin(),
119  [](const TGNode *a) {
120  return a->c->edge->id;
121  });
122  std::vector<int> indices;
123  C_Path cpath = build_cpath(tg_opath, &indices);
124  SPDLOG_TRACE("Opath is {}", opath);
125  SPDLOG_TRACE("Indices is {}", indices);
126  SPDLOG_TRACE("Complete path is {}", cpath);
127  LineString mgeom = network_.complete_path_to_geometry(
128  traj.geom, cpath);
129  return MatchResult{
130  traj.id, matched_candidate_path, opath, cpath, indices, mgeom};
131 }
132 
133 void STMATCH::update_tg(TransitionGraph *tg,
134  const CompositeGraph &cg,
135  const Trajectory &traj,
136  const STMATCHConfig &config) {
137  SPDLOG_TRACE("Update transition graph");
138  std::vector<TGLayer> &layers = tg->get_layers();
139  std::vector<double> eu_dists = ALGORITHM::cal_eu_dist(traj.geom);
140  int N = layers.size();
141  for (int i = 0; i < N - 1; ++i) {
142  // Routing from current_layer to next_layer
143  SPDLOG_TRACE("Update layer {} ", i);
144  double delta = 0;
145  if (traj.timestamps.size() != N) {
146  delta = eu_dists[i] * config.factor * 4;
147  } else {
148  double duration = traj.timestamps[i + 1] - traj.timestamps[i];
149  delta = config.factor * config.vmax * duration;
150  }
151  update_layer(i, &(layers[i]), &(layers[i + 1]),
152  cg, eu_dists[i], delta);
153  }
154  SPDLOG_TRACE("Update transition graph done");
155 }
156 
157 void STMATCH::update_layer(int level, TGLayer *la_ptr, TGLayer *lb_ptr,
158  const CompositeGraph &cg,
159  double eu_dist,
160  double delta) {
161  // SPDLOG_TRACE("Update layer");
162  TGLayer &lb = *lb_ptr;
163  for (auto iter = la_ptr->begin(); iter != la_ptr->end(); ++iter) {
164  NodeIndex source = iter->c->index;
165  SPDLOG_TRACE(" Calculate distance from source {}", source);
166  // single source upper bound routing
167  std::vector<NodeIndex> targets(lb.size());
168  std::transform(lb.begin(), lb.end(), targets.begin(),
169  [](TGNode &a) {
170  return a.c->index;
171  });
172  SPDLOG_TRACE(" Upperbound shortest path {} ", delta);
173  std::vector<double> distances = shortest_path_upperbound(
174  level, cg, source, targets, delta);
175  SPDLOG_TRACE(" Update property of transition graph ");
176  for (int i = 0; i < distances.size(); ++i) {
177  double tp = TransitionGraph::calc_tp(distances[i], eu_dist);
178  if (lb[i].cumu_prob < iter->cumu_prob + tp * lb[i].ep) {
179  lb[i].cumu_prob = iter->cumu_prob + tp * lb[i].ep;
180  lb[i].prev = &(*iter);
181  lb[i].tp = tp;
182  lb[i].sp_dist = distances[i];
183  }
184  }
185  }
186  SPDLOG_TRACE("Update layer done");
187 }
188 
189 std::vector<double> STMATCH::shortest_path_upperbound(
190  int level, const CompositeGraph &cg, NodeIndex source,
191  const std::vector<NodeIndex> &targets, double delta) {
192  SPDLOG_TRACE("Upperbound shortest path source {}", source);
193  SPDLOG_TRACE("Upperbound shortest path targets {}", targets);
194  std::unordered_set<NodeIndex> unreached_targets;
195  for (auto &node:targets) {
196  unreached_targets.insert(node);
197  }
198  DistanceMap dmap;
199  PredecessorMap pmap;
200  Heap Q;
201  Q.push(source, 0);
202  pmap.insert({source, source});
203  dmap.insert({source, 0});
204  double temp_dist = 0;
205  // Dijkstra search
206  while (!Q.empty() && !unreached_targets.empty()) {
207  HeapNode node = Q.top();
208  Q.pop();
209  SPDLOG_TRACE(" Node u {} dist {}", node.index, node.value);
210  NodeIndex u = node.index;
211  auto iter = unreached_targets.find(u);
212  if (iter != unreached_targets.end()) {
213  // Remove u
214  SPDLOG_TRACE(" Remove target {}", u);
215  unreached_targets.erase(iter);
216  }
217  if (node.value > delta) break;
218  std::vector<CompEdgeProperty> out_edges = cg.out_edges(u);
219  for (auto node_iter = out_edges.begin(); node_iter != out_edges.end();
220  ++node_iter) {
221  NodeIndex v = node_iter->v;
222  temp_dist = node.value + node_iter->cost;
223  SPDLOG_TRACE(" Examine node v {} temp dist {}", v, temp_dist);
224  auto v_iter = dmap.find(v);
225  if (v_iter != dmap.end()) {
226  // dmap contains node v
227  if (v_iter->second - temp_dist > 1e-6) {
228  // a smaller distance is found for v
229  SPDLOG_TRACE(" Update key {} {} in pdmap prev dist {}",
230  v, temp_dist, v_iter->second);
231  pmap[v] = u;
232  dmap[v] = temp_dist;
233  Q.decrease_key(v, temp_dist);
234  }
235  } else {
236  // dmap does not contain v
237  if (temp_dist <= delta) {
238  SPDLOG_TRACE(" Insert key {} {} into pmap and dmap",
239  v, temp_dist);
240  Q.push(v, temp_dist);
241  pmap.insert({v, u});
242  dmap.insert({v, temp_dist});
243  }
244  }
245  }
246  }
247  // Update distances
248  SPDLOG_TRACE(" Update distances");
249  std::vector<double> distances;
250  for (int i = 0; i < targets.size(); ++i) {
251  if (dmap.find(targets[i]) != dmap.end()) {
252  distances.push_back(dmap[targets[i]]);
253  } else {
254  distances.push_back(std::numeric_limits<double>::max());
255  }
256  }
257  SPDLOG_TRACE(" Distance value {}", distances);
258  return distances;
259 }
260 
261 C_Path STMATCH::build_cpath(const TGOpath &opath, std::vector<int> *indices) {
262  SPDLOG_DEBUG("Build cpath from optimal candidate path");
263  C_Path cpath;
264  if (!indices->empty()) indices->clear();
265  if (opath.empty()) return cpath;
266  const std::vector<Edge> &edges = network_.get_edges();
267  int N = opath.size();
268  cpath.push_back(opath[0]->c->edge->id);
269  int current_idx = 0;
270  SPDLOG_TRACE("Insert index {}", current_idx);
271  indices->push_back(current_idx);
272  for (int i = 0; i < N - 1; ++i) {
273  const Candidate *a = opath[i]->c;
274  const Candidate *b = opath[i + 1]->c;
275  SPDLOG_TRACE("Check a {} b {}", a->edge->id, b->edge->id);
276  if ((a->edge->id != b->edge->id) || (a->offset > b->offset)) {
277  auto segs = graph_.shortest_path_dijkstra(a->edge->target,
278  b->edge->source);
279  // No transition found
280  if (segs.empty() && a->edge->target != b->edge->source) {
281  indices->clear();
282  return {};
283  }
284  SPDLOG_TRACE("Edges found {}", segs);
285  for (int e:segs) {
286  cpath.push_back(edges[e].id);
287  ++current_idx;
288  }
289  cpath.push_back(b->edge->id);
290  ++current_idx;
291  SPDLOG_TRACE("Insert index {}", current_idx);
292  indices->push_back(current_idx);
293  } else {
294  SPDLOG_TRACE("Insert index {}", current_idx);
295  indices->push_back(current_idx);
296  }
297  }
298  SPDLOG_DEBUG("Build cpath from optimal candidate path done");
299  return cpath;
300 }
FMM::MM::TransitionGraph
Transition graph class in HMM.
Definition: transition_graph.hpp:54
FMM::MM::MatchedCandidatePath
std::vector< MatchedCandidate > MatchedCandidatePath
A vector of candidates, used for representing the matching result of a trajectory.
Definition: mm_type.hpp:64
FMM::CORE::Trajectory::geom
LineString geom
Geometry of the trajectory.
Definition: gps.hpp:28
FMM::MM::STMATCHConfig::factor
double factor
factor multiplied to vmax*deltaT to limit the search of shortest path
Definition: stmatch_algorithm.hpp:48
FMM::MM::STMATCHConfig::load_from_arg
static STMATCHConfig load_from_arg(const cxxopts::ParseResult &arg_data)
Load from argument parsed data.
Definition: stmatch_algorithm.cpp:37
FMM::PYTHON::PyMatchResult::mgeom
CORE::LineString mgeom
Geometry of the matched path.
Definition: pyfmm.hpp:45
FMM::NETWORK::Heap
Heap data structure used in the routing query
Definition: heap.hpp:40
FMM::MM::Traj_Candidates
std::vector< Point_Candidates > Traj_Candidates
trajectory candidates
Definition: mm_type.hpp:38
FMM::CORE::LineString::add_point
void add_point(double x, double y)
Add a point to the end of the current line.
Definition: geometry.hpp:79
FMM::MM::Candidate::point
FMM::CORE::Point point
boost point
Definition: mm_type.hpp:34
FMM::MM::TGOpath
std::vector< const TGNode * > TGOpath
The optimal path of nodes in the transition graph.
Definition: transition_graph.hpp:46
FMM::MM::STMATCHConfig::vmax
double vmax
maximum speed of the vehicle, unit is map_unit/second
Definition: stmatch_algorithm.hpp:47
FMM::CORE::Trajectory::timestamps
std::vector< double > timestamps
Timestamps of the trajectory.
Definition: gps.hpp:29
FMM::NETWORK::Heap::decrease_key
void decrease_key(NodeIndex index, double value)
Decrease a node in the heap.
Definition: heap.hpp:93
FMM::MM::Candidate::edge
NETWORK::Edge * edge
candidate edge
Definition: mm_type.hpp:33
FMM::MM::MatchedCandidate::c
Candidate c
Candidate matched to a point.
Definition: mm_type.hpp:54
FMM::NETWORK::Heap::top
HeapNode top()
Get a copy of the top node in the heap.
Definition: heap.hpp:63
FMM::MM::MatchResult
Map matched result representation.
Definition: mm_type.hpp:69
FMM::NETWORK::Heap::push
void push(NodeIndex index, double value)
Push a node into the heap.
Definition: heap.hpp:47
FMM::NETWORK::HeapNode
Node in the heap structure.
Definition: heap.hpp:22
FMM::PYTHON::PyMatchResult::candidates
std::vector< PyCandidate > candidates
Candidate matched to each point.
Definition: pyfmm.hpp:43
FMM::NETWORK::HeapNode::index
NodeIndex index
Index of a node in the heap.
Definition: heap.hpp:24
FMM::MM::STMATCHConfig::gps_error
double gps_error
GPS error, unit is map_unit.
Definition: stmatch_algorithm.hpp:46
FMM::MM::TransitionGraph::backtrack
TGOpath backtrack()
Backtrack the transition graph to find an optimal path.
Definition: transition_graph.cpp:62
FMM::MM::STMATCHConfig::radius
double radius
search radius for candidates, unit is map_unit
Definition: stmatch_algorithm.hpp:45
FMM::NETWORK::Edge::id
EdgeID id
Edge ID, can be discontinuous integers.
Definition: type.hpp:48
FMM::NETWORK::DistanceMap
std::unordered_map< NodeIndex, double > DistanceMap
Distance map.
Definition: graph.hpp:61
FMM::PYTHON::PyMatchResult
POD Match result type used in Python API.
Definition: pyfmm.hpp:39
FMM::MM::C_Path
std::vector< FMM::NETWORK::EdgeID > C_Path
Complete path, ids of a sequence of topologically connected edges.
Definition: mm_type.hpp:47
FMM::MM::Candidate::dist
double dist
distance from original point p to map matched point p'
Definition: mm_type.hpp:32
FMM::MM::MatchResult::cpath
C_Path cpath
the complete path, containing ids of a sequence of topologically connected edges traversed by the tra...
Definition: mm_type.hpp:77
FMM::MM::STMATCHConfig::print
void print() const
Print configuration data.
Definition: stmatch_algorithm.cpp:21
FMM::MM::STMATCHConfig::load_from_xml
static STMATCHConfig load_from_xml(const boost::property_tree::ptree &xml_data)
Load from xml data.
Definition: stmatch_algorithm.cpp:27
FMM
Fast map matching.
Definition: geom_algorithm.hpp:17
FMM::PYTHON::PyMatchResult::indices
std::vector< int > indices
index of matched edge in the cpath
Definition: pyfmm.hpp:44
FMM::CORE::Trajectory
Trajectory class
Definition: gps.hpp:26
FMM::MM::CompositeGraph::out_edges
std::vector< CompEdgeProperty > out_edges(NETWORK::NodeIndex u) const
Get out edges leaving a node u in the composite graph.
Definition: composite_graph.cpp:146
FMM::NETWORK::Network::search_tr_cs_knn
FMM::MM::Traj_Candidates search_tr_cs_knn(FMM::CORE::Trajectory &trajectory, std::size_t k, double radius) const
Search for k nearest neighboring (KNN) candidates of a trajectory within a search radius.
Definition: network.cpp:189
FMM::PYTHON::PyMatchResult::pgeom
CORE::LineString pgeom
Point position matched for each GPS point.
Definition: pyfmm.hpp:46
FMM::MM::MatchResult::mgeom
CORE::LineString mgeom
the geometry of the matched path
Definition: mm_type.hpp:81
FMM::MM::MatchResult::id
int id
id of the trajectory to be matched
Definition: mm_type.hpp:70
FMM::MM::Candidate
Candidate edge matched to a GPS point
Definition: mm_type.hpp:26
FMM::NETWORK::Heap::pop
void pop()
Pop a node from the heap.
Definition: heap.hpp:54
FMM::NETWORK::Heap::empty
bool empty()
Check if the heap is empty.
Definition: heap.hpp:70
FMM::MM::MatchedCandidate::tp
double tp
transition probability to previous matched candidate
Definition: mm_type.hpp:56
FMM::MM::Candidate::offset
double offset
offset distance from the start of polyline to p'
Definition: mm_type.hpp:31
FMM::CORE
Core data types.
Definition: geometry.hpp:22
FMM::MM::STMATCH::update_tg
void update_tg(TransitionGraph *tg, const CompositeGraph &cg, const CORE::Trajectory &traj, const STMATCHConfig &config)
Update probabilities in a transition graph.
Definition: stmatch_algorithm.cpp:133
FMM::PYTHON::PyMatchResult::opath
MM::O_Path opath
Edge ID matched for each point of the trajectory
Definition: pyfmm.hpp:41
FMM::CORE::LineString
Linestring geometry class.
Definition: geometry.hpp:34
FMM::MM::MatchResult::opt_candidate_path
MatchedCandidatePath opt_candidate_path
A vector of candidate matched to each point of a trajectory.
Definition: mm_type.hpp:71
FMM::MM
Class related with map matching.
Definition: composite_graph.hpp:18
FMM::MM::MatchResult::opath
O_Path opath
the optimal path, containing id of edges matched to each point in a trajectory
Definition: mm_type.hpp:74
FMM::NETWORK
Classes related with network and graph.
Definition: graph.hpp:21
FMM::MM::O_Path
std::vector< FMM::NETWORK::EdgeID > O_Path
Optimal path, edge id matched to each point in the trajectory.
Definition: mm_type.hpp:44
FMM::MM::DummyGraph
A graph containing dummy nodes and edges used in map matching.
Definition: composite_graph.hpp:31
FMM::MM::CompositeGraph
Composite Graph as a wrapper of network graph and dummy graph.
Definition: composite_graph.hpp:136
FMM::NETWORK::NetworkGraph::get_node_id
int get_node_id(NodeIndex idx) const
Get node ID from a node index.
Definition: network_graph.hpp:117
FMM::MM::STMATCHConfig
Configuration of stmatch algorithm.
Definition: stmatch_algorithm.hpp:31
FMM::PYTHON::PyMatchResult::id
int id
id of a trajectory
Definition: pyfmm.hpp:40
FMM::CORE::wkt2linestring
LineString wkt2linestring(const std::string &wkt)
Convert a wkt into a linestring.
Definition: geometry.cpp:42
FMM::MM::MatchResult::indices
std::vector< int > indices
index of opath edge in cpath
Definition: mm_type.hpp:80
FMM::MM::MatchedCandidate
A candidate matched to a point.
Definition: mm_type.hpp:53
FMM::MM::TransitionGraph::get_layers
std::vector< TGLayer > & get_layers()
Get a reference to the inner layers of the transition graph.
Definition: transition_graph.cpp:86
FMM::NETWORK::Edge::source
NodeIndex source
source node index
Definition: type.hpp:49
FMM::NETWORK::NodeIndex
unsigned int NodeIndex
Node Index in the network, range from [0,num_vertices-1 ].
Definition: type.hpp:24
FMM::ALGORITHM::cal_eu_dist
std::vector< double > cal_eu_dist(const FMM::CORE::LineString &trajectory)
Calculate segment length of a trajectory.
Definition: geom_algorithm.cpp:7
FMM::PYTHON::PyMatchResult::cpath
MM::C_Path cpath
Edge ID traversed by the matched path.
Definition: pyfmm.hpp:42
FMM::CORE::LineString::get_num_points
int get_num_points() const
Get the number of points in a line.
Definition: geometry.hpp:115
FMM::MM::STMATCH::match_wkt
PYTHON::PyMatchResult match_wkt(const std::string &wkt, const STMATCHConfig &config)
Match a wkt linestring to the road network.
Definition: stmatch_algorithm.cpp:56
FMM::NETWORK::Edge::length
double length
length of the edge polyline
Definition: type.hpp:51
FMM::MM::STMATCH::match_traj
MatchResult match_traj(const CORE::Trajectory &traj, const STMATCHConfig &config)
Match a trajectory to the road network.
Definition: stmatch_algorithm.cpp:88
FMM::NETWORK::PredecessorMap
std::unordered_map< NodeIndex, NodeIndex > PredecessorMap
Predecessor Map.
Definition: graph.hpp:55
FMM::MM::TGNode
A node in the transition graph.
Definition: transition_graph.hpp:29
FMM::MM::MatchedCandidate::sp_dist
double sp_dist
shortest path distance to previous matched candidate
Definition: mm_type.hpp:57
FMM::MM::TGLayer
std::vector< TGNode > TGLayer
A layer of nodes in the transition graph.
Definition: transition_graph.hpp:42
FMM::PYTHON
Data type for Python API.
Definition: pyfmm.hpp:19
FMM::NETWORK::HeapNode::value
double value
Value of a node in the heap.
Definition: heap.hpp:25
FMM::NETWORK::Edge::target
NodeIndex target
target node index
Definition: type.hpp:50
FMM::MM::STMATCHConfig::k
int k
number of candidates
Definition: stmatch_algorithm.hpp:44
FMM::MM::MatchedCandidate::ep
double ep
emission probability
Definition: mm_type.hpp:55
FMM::MM::STMATCHConfig::validate
bool validate() const
Check the validity of the configuration.
Definition: stmatch_algorithm.cpp:47