3 #include "ceres/ceres.h"
4 #include "gflags/gflags.h"
5 #include "glog/logging.h"
6 #include "LatLong-UTMconversion.h"
10 #define GPS_EXTRA_SIGMA_PER_SECOND 1.0
12 #define KD_LEAF_SIZE 5
14 using namespace ceres;
26 double utm_north, utm_east;
42 double len_sq, inv_len_sq;
44 vector<LineSegment> roads;
47 enum Axis { LEAF, X_AXIS, Y_AXIS } split_axis;
49 vector<int> roads_this_node;
50 KDTreeNode *left, *right;
52 KDTreeNode *root = NULL;
54 double signed_distance(KDTreeNode::Axis split_axis, double split_pos, Coord c)
56 if (split_axis == KDTreeNode::X_AXIS) {
57 return c.x - split_pos;
59 return c.y - split_pos;
63 double split_cost(const vector<int>& remaining_roads, KDTreeNode::Axis split_axis, double split_pos)
65 unsigned in_left = 0, in_right = 0, in_both = 0;
66 for (int i = 0; i < remaining_roads.size(); ++i) {
67 double dist_from = signed_distance(split_axis, split_pos, roads[remaining_roads[i]].from);
68 double dist_to = signed_distance(split_axis, split_pos, roads[remaining_roads[i]].to);
69 if (dist_from > 0.0 && dist_to > 0.0) {
71 } else if (dist_from < 0.0 && dist_to < 0.0) {
78 return max(in_left, in_right) + 1.1 * in_both;
81 KDTreeNode* make_kd_tree(const vector<int>& remaining_roads, KDTreeNode::Axis split_axis)
83 KDTreeNode *node = new KDTreeNode;
84 if (remaining_roads.size() <= KD_LEAF_SIZE) {
85 node->split_axis = KDTreeNode::LEAF;
86 node->left = node->right = NULL;
87 node->roads_this_node = remaining_roads;
91 node->split_axis = split_axis;
94 vector<double> all_points;
95 KDTreeNode::Axis next_axis;
96 if (split_axis == KDTreeNode::X_AXIS) {
97 for (unsigned i = 0; i < remaining_roads.size(); ++i) {
98 all_points.push_back(roads[remaining_roads[i]].from.x - 1e-6);
99 all_points.push_back(roads[remaining_roads[i]].to.x - 1e-6);
100 all_points.push_back(roads[remaining_roads[i]].from.x + 1e-6);
101 all_points.push_back(roads[remaining_roads[i]].to.x + 1e-6);
103 next_axis = KDTreeNode::Y_AXIS;
105 for (unsigned i = 0; i < remaining_roads.size(); ++i) {
106 all_points.push_back(roads[remaining_roads[i]].from.y - 1e-6);
107 all_points.push_back(roads[remaining_roads[i]].to.y - 1e-6);
108 all_points.push_back(roads[remaining_roads[i]].from.y + 1e-6);
109 all_points.push_back(roads[remaining_roads[i]].to.y + 1e-6);
111 next_axis = KDTreeNode::X_AXIS;
113 sort(all_points.begin(), all_points.end());
115 vector<double>::iterator new_end = unique(all_points.begin(), all_points.end());
116 all_points.resize(new_end - all_points.begin());
118 // now try all split points
119 double best_pos = 0.0;
120 double best_cost = HUGE_VAL;
122 for (unsigned i = 0; i < all_points.size(); ++i) {
123 double cost = split_cost(remaining_roads, split_axis, all_points[i]);
124 if (cost < best_cost) {
125 best_pos = all_points[i];
129 node->split_pos = best_pos;
131 vector<int> left, right;
132 for (unsigned i = 0; i < remaining_roads.size(); ++i) {
133 double dist_from = signed_distance(split_axis, best_pos, roads[remaining_roads[i]].from);
134 double dist_to = signed_distance(split_axis, best_pos, roads[remaining_roads[i]].to);
135 if (dist_from > 0.0 && dist_to > 0.0) {
136 right.push_back(remaining_roads[i]);
137 } else if (dist_from < 0.0 && dist_to < 0.0) {
138 left.push_back(remaining_roads[i]);
140 node->roads_this_node.push_back(remaining_roads[i]);
144 if (left.size() == 0) {
147 node->left = make_kd_tree(left, next_axis);
149 if (right.size() == 0) {
152 node->right = make_kd_tree(right, next_axis);
157 void plot_kd_tree(FILE *fp, KDTreeNode *node, double min_x, double min_y, double max_x, double max_y) {
158 if (node->split_axis == KDTreeNode::X_AXIS) {
160 fprintf(fp, "%f %f %f %f\n", node->split_pos, min_y, node->split_pos, max_y);
161 if (node->left != NULL) {
162 plot_kd_tree(fp, node->left, min_x, min_y, node->split_pos, max_y);
164 if (node->right != NULL) {
165 plot_kd_tree(fp, node->right, node->split_pos, min_y, max_x, max_y);
168 if (node->split_axis == KDTreeNode::Y_AXIS) {
170 fprintf(fp, "%f %f %f %f\n", min_x, node->split_pos, max_x, node->split_pos);
171 if (node->left != NULL) {
172 plot_kd_tree(fp, node->left, min_x, min_y, max_x, node->split_pos);
174 if (node->right != NULL) {
175 plot_kd_tree(fp, node->right, min_x, node->split_pos, max_x, max_y);
180 char* write_time(time_t t)
182 static char buf[256];
183 struct tm *foo = gmtime(&t);
184 strftime(buf, 256, "%Y-%m-%dT%H:%M:%SZ", foo);
188 class MinimizeAccelerationCostFunction {
190 MinimizeAccelerationCostFunction() {}
193 bool operator() (const T * const p0, const T * const p1, const T * const p2, T* e) const {
194 e[0] = p0[0] - T(2.0) * p1[0] + p2[0];
195 e[1] = p0[1] - T(2.0) * p1[1] + p2[1];
200 double sq_distance_point_point(const Coord& a, const Coord& b)
202 //return hypot(b.x - a.x, b.y - a.y);
203 return (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
206 double sq_distance_point_line_segment(const Coord& c, LineSegment &ls)
208 if (ls.len_sq < 1e-6) {
209 return sq_distance_point_point(c, ls.from);
211 const double pv_x = c.x - ls.from.x;
212 const double pv_y = c.y - ls.from.y;
213 const double t = (pv_x * ls.wv_x + pv_y * ls.wv_y) * ls.inv_len_sq;
215 return sq_distance_point_point(c, ls.from);
218 return sq_distance_point_point(c, ls.to);
222 r.x = ls.from.x + t * ls.wv_x;
223 r.y = ls.from.y + t * ls.wv_y;
224 return sq_distance_point_point(c, r);
227 pair<int, double> find_closest_road_nonkd(Coord c)
230 double sq_best_distance = HUGE_VAL;
231 for (unsigned i = 0; i < roads.size(); ++i) {
232 double sq_distance = sq_distance_point_line_segment(c, roads[i]);
233 if (sq_distance < sq_best_distance) {
235 sq_best_distance = sq_distance;
238 return make_pair(best_idx, std::sqrt(sq_best_distance));
242 KDTreeJob(KDTreeNode* node, KDTreeNode* parent) : node(node), parent(parent) {}
243 KDTreeJob(const KDTreeJob& other) : node(other.node), parent(other.parent) {}
248 vector<KDTreeJob> kd_todo;
249 //vector<KDTreeNode*> kd_todo;
251 pair<int, double> find_closest_road(Coord c)
254 double sq_best_distance = HUGE_VAL;
257 kd_todo.push_back(KDTreeJob(root, NULL));
260 while (kd_todo.size() > kd_pos) {
261 KDTreeJob job = kd_todo[kd_pos++];
262 KDTreeNode* node = job.node;
264 // Re-verify versus sq_best_distance here, since it might have changed.
265 if (job.parent != NULL) {
266 double dist = signed_distance(job.parent->split_axis, job.parent->split_pos, c);
267 if (dist*dist > sq_best_distance) {
268 // OK, later changes to best_distance has rendered this job moot.
274 // Check all nodes that are interior to this.
275 for (unsigned i = 0; i < node->roads_this_node.size(); ++i) {
276 int idx = node->roads_this_node[i];
277 double sq_distance = sq_distance_point_line_segment(c, roads[idx]);
278 if (sq_distance < sq_best_distance || (sq_distance == sq_best_distance && idx < best_idx)) {
280 sq_best_distance = sq_distance;
284 if (node->split_axis == KDTreeNode::LEAF) {
288 double dist = signed_distance(node->split_axis, node->split_pos, c);
289 if (dist * dist <= sq_best_distance) {
290 // gah! we have to put both in there.
291 // try at least the correct side first.
293 if (node->left != NULL) {
294 kd_todo.push_back(KDTreeJob(node->left, node));
301 if (node->right != NULL) {
302 kd_todo.push_back(KDTreeJob(node->right, node));
310 // woo, we can do with going on only one side
312 // go on to the right side
320 // go on to the left side
330 return make_pair(best_idx, std::sqrt(sq_best_distance));
333 class DistanceFromRoadCostFunction : public SizedCostFunction<1, 2, 2> {
335 virtual bool Evaluate(double const* const* p,
337 double** jacobians) const {
338 (void) jacobians; // Ignored; filled in by numeric differentiation.
348 pair<int, double> closest_road = find_closest_road(c);
350 // verify kd behavior
351 pair<int, double> closest_road_nonkd = find_closest_road_nonkd(c);
352 if (closest_road.first != closest_road_nonkd.first) {
353 printf("oops! for <%f,%f> kd=(%d,%.10f) and nonkd=(%d,%.10f)\n",
354 c.x, c.y, closest_road.first, closest_road.second,
355 closest_road_nonkd.first, closest_road_nonkd.second);
356 printf("kd:\n%f %f\n%f %f\n",
357 roads[closest_road.first].from.x,
358 roads[closest_road.first].from.y,
359 roads[closest_road.first].to.x,
360 roads[closest_road.first].to.y);
361 printf("Nd:\n%f %f\n%f %f\n",
362 roads[closest_road_nonkd.first].from.x,
363 roads[closest_road_nonkd.first].from.y,
364 roads[closest_road_nonkd.first].to.x,
365 roads[closest_road_nonkd.first].to.y);
369 double prev_distance = std::sqrt(sq_distance_point_line_segment(prev_c, roads[closest_road.first]));
372 //e[0] = 2.0 - std::exp(-(best_distance * best_distance)) - std::exp(-(prev_distance * prev_distance));
373 //e[0] = 0.2 * (best_distance + 0.5 * prev_distance);
374 //e[0] = ceres::log(best_distance);
375 e[0] = closest_road.second + 0.5 * prev_distance;
380 class FootDiffCostFunction {
382 FootDiffCostFunction(double speed) : speed_(speed) {}
385 bool operator() (const T * const p0, const T * const p1, T* e) const {
386 T ex = p1[0] - p0[0];
387 T ey = p1[1] - p0[1];
388 T speed = 0.5 * ceres::sqrt(ex * ex + ey * ey);
389 e[0] = speed - speed_;
397 class GPSDiffCostFunction {
399 GPSDiffCostFunction(double x, double y) : x_(x), y_(y) {}
402 bool operator() (const T * const p, T* e) const {
403 T x_dist = p[0] - T(x_);
404 T y_dist = p[1] - T(y_);
405 T sq_dist = x_dist * x_dist + y_dist * y_dist;
406 e[0] = T(10.0) * (T(1.0) - ceres::exp(-sq_dist / T(GPS_SIGMA * GPS_SIGMA)));
407 //e[0] = sq_dist / GPS_SIGMA;
416 class MyIterationCallback : public IterationCallback {
418 MyIterationCallback(Coord *c, int num_points) : c_(c), num_points_(num_points) {}
420 virtual CallbackReturnType operator()(const IterationSummary& summary)
423 sprintf(buf, "temp%04d.plot", summary.iteration);
424 FILE *fp = fopen(buf, "w");
425 for (int i = 0; i < num_points_; ++i) {
426 fprintf(fp, "%f %f\n", c_[i].x, c_[i].y);
429 return SOLVER_CONTINUE;
438 int main(int argc, char** argv) {
439 google::ParseCommandLineFlags(&argc, &argv, true);
440 google::InitGoogleLogging(argv[0]);
442 int max_time = INT_MIN, min_time = INT_MAX;
444 vector<TCXPoint> tcx_points;
445 vector<FootPoint> foot_points;
447 FILE *fp = fopen(argv[1], "r");
455 if (fgets(buf, 256, fp) == NULL) {
458 if (strncmp(buf, "tcx ", 4) == 0) {
460 if (sscanf(buf, "tcx %d %lf %lf %lf %d", &p.time, &p.lat, &p.lon, &p.alt, &p.hr) != 5) {
461 fprintf(stderr, "parse error: %s\n", buf);
465 LLtoUTM(23, p.lat * rad2deg, p.lon * rad2deg, p.utm_north, p.utm_east, p.utm_zone);
467 tcx_points.push_back(p);
468 max_time = max(max_time, p.time);
469 min_time = min(min_time, p.time);
472 if (strncmp(buf, "footpod ", 8) == 0) {
474 if (sscanf(buf, "footpod %d %d %d %lf %lf", &p.time, &p.hr, &p.cadence, &p.speed, &p.distance) != 5) {
475 fprintf(stderr, "parse error: %s\n", buf);
478 foot_points.push_back(p);
479 max_time = max(max_time, p.time);
480 min_time = min(min_time, p.time);
486 // normalize the utm guys a bit
487 double utm_north_sum = 0.0, utm_east_sum = 0.0;
488 for (unsigned i = 0; i < tcx_points.size(); ++i) {
489 utm_north_sum += tcx_points[i].utm_north;
490 utm_east_sum += tcx_points[i].utm_east;
492 utm_north_sum /= tcx_points.size();
493 utm_east_sum /= tcx_points.size();
494 for (unsigned i = 0; i < tcx_points.size(); ++i) {
495 tcx_points[i].utm_north -= utm_north_sum;
496 tcx_points[i].utm_east -= utm_east_sum;
500 fp = fopen(argv[2], "r");
507 double lat1, lon1, lat2, lon2;
508 if (fscanf(fp, "%lf %lf %lf %lf\n", &lat1, &lon1, &lat2, &lon2) != 4) {
514 LLtoUTM(23, lat1, lon1, seg.from.y, seg.from.x, utm_zone);
515 LLtoUTM(23, lat2, lon2, seg.to.y, seg.to.x, utm_zone);
517 seg.from.y -= utm_north_sum;
518 seg.from.x -= utm_east_sum;
519 seg.to.y -= utm_north_sum;
520 seg.to.x -= utm_east_sum;
522 seg.len_sq = (seg.to.y - seg.from.y) * (seg.to.y - seg.from.y) + (seg.to.x - seg.from.x) * (seg.to.x - seg.from.x);
523 seg.inv_len_sq = 1.0 / seg.len_sq;
524 seg.wv_x = seg.to.x - seg.from.x;
525 seg.wv_y = seg.to.y - seg.from.y;
527 roads.push_back(seg);
531 vector<int> all_road_indexes;
532 for (unsigned i = 0; i < roads.size(); ++i) {
533 all_road_indexes.push_back(i);
535 root = make_kd_tree(all_road_indexes, KDTreeNode::X_AXIS);
536 //root = make_kd_tree(all_road_indexes, KDTreeNode::Y_AXIS);
538 fp = fopen("kdtree.plot", "w");
539 plot_kd_tree(fp, root, -700.0, -2100.0, 800.0, 1800.0);
542 fp = fopen("map.plot", "w");
543 for (unsigned i = 0; i < roads.size(); ++i) {
544 fprintf(fp, "%f %f %f %f\n", roads[i].from.x, roads[i].from.y, roads[i].to.x, roads[i].to.y);
548 int num_points = max_time - min_time + 1;
553 // Seed the initial path with some guesses.
554 for (int t = min_time; t <= max_time; ++t) {
555 // find the closest TCX point (yeah, yeah, linear search...)
557 int best_distance = INT_MAX;
558 for (unsigned i = 0; i < tcx_points.size(); ++i) {
559 int distance = abs(t - tcx_points[i].time);
560 if (distance < best_distance) {
562 best_distance = distance;
566 c[t - min_time].x = tcx_points[best_idx].utm_east + (rand() % 1000) / 1000.0;
567 c[t - min_time].y = tcx_points[best_idx].utm_north + (rand() % 1000) / 1000.0;
570 // Create regularization constraints.
571 for (int t = min_time + 1; t <= max_time - 1; ++t) {
572 problem.AddResidualBlock(
573 new AutoDiffCostFunction<MinimizeAccelerationCostFunction, 2, 2, 2, 2>(
574 new MinimizeAccelerationCostFunction),
576 reinterpret_cast<double *>(&c[(t - min_time) - 1]),
577 reinterpret_cast<double *>(&c[(t - min_time) + 0]),
578 reinterpret_cast<double *>(&c[(t - min_time) + 1]));
581 // Create close-to-road constraints.
582 for (int t = min_time + 1; t <= max_time; ++t) {
583 problem.AddResidualBlock(
584 new NumericDiffCostFunction<DistanceFromRoadCostFunction, FORWARD, 1, 2, 2>(
585 new DistanceFromRoadCostFunction,
586 ceres::TAKE_OWNERSHIP),
588 reinterpret_cast<double *>(&c[t - min_time]),
589 reinterpret_cast<double *>(&c[t - min_time - 1]));
592 // Now create one contraint per GPS point.
593 for (unsigned i = 0; i < tcx_points.size(); ++i) {
594 int t = tcx_points[i].time;
595 problem.AddResidualBlock(
596 new AutoDiffCostFunction<GPSDiffCostFunction, 1, 2>(
597 new GPSDiffCostFunction(tcx_points[i].utm_east, tcx_points[i].utm_north)),
599 reinterpret_cast<double *>(&c[t - min_time]));
602 // And per foot point.
603 for (unsigned i = 0; i < foot_points.size(); ++i) {
604 int t = foot_points[i].time;
605 if (t - 1 < min_time || t + 1 >= max_time) {
608 problem.AddResidualBlock(
609 new AutoDiffCostFunction<FootDiffCostFunction, 1, 2, 2>(
610 new FootDiffCostFunction(foot_points[i].speed)),
612 reinterpret_cast<double *>(&c[(t - min_time) - 1]),
613 reinterpret_cast<double *>(&c[(t - min_time) + 1]));
616 printf("pushed %d+%d points\n", int(tcx_points.size()), int(foot_points.size()));
617 printf("time from %d to %d\n", min_time, max_time);
620 Solver::Options options;
621 options.max_num_iterations = 1000000;
622 options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
623 //options.linear_solver_type = ceres::DENSE_QR;
624 options.minimizer_progress_to_stdout = true;
627 options.update_state_every_iteration = true;
628 options.callbacks.push_back(new MyIterationCallback(c, num_points));
630 Solver::Summary summary;
631 Solve(options, &problem, &summary);
632 // if (summary.termination_type == NUMERICAL_FAILURE) {
636 fp = fopen("out.plot", "w");
637 for (int t = min_time; t <= max_time; ++t) {
638 fprintf(fp, "%d %f %f\n", t, c[t - min_time].x, c[t - min_time].y);
642 fp = fopen("out.tcx", "w");
643 fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
644 fprintf(fp, "<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n");
645 fprintf(fp, " <Activities>\n");
646 fprintf(fp, " <Activity Sport=\"Running\">\n");
647 fprintf(fp, " <Id>smoothed</Id>\n");
648 fprintf(fp, " <Lap StartTime=\"%s\">\n", write_time(min_time));
649 fprintf(fp, " <TotalTimeSeconds>%f</TotalTimeSeconds>\n", float(max_time - min_time + 1));
650 fprintf(fp, " <DistanceMeters>3720.0</DistanceMeters>\n"); // FIXME
651 fprintf(fp, " <Intensity>Active</Intensity>\n");
652 fprintf(fp, " <TriggerMethod>Manual</TriggerMethod>\n");
653 fprintf(fp, " <Track>\n");
655 for (int t = min_time; t <= max_time; ++t) {
657 double north = c[t - min_time].y + utm_north_sum;
658 double east = c[t - min_time].x + utm_east_sum;
662 tcx_points[0].utm_zone,
664 fprintf(fp, " <TrackPoint>\n");
665 fprintf(fp, " <Time>%s</Time>\n", write_time(t));
666 fprintf(fp, " <Position>\n");
667 fprintf(fp, " <LatitudeDegrees>%.10f</LatitudeDegrees>\n", lat);
668 fprintf(fp, " <LongitudeDegrees>%.10f</LongitudeDegrees>\n", lon);
669 fprintf(fp, " </Position>\n");
670 fprintf(fp, " </TrackPoint>\n");
672 fprintf(fp, " </Track>\n");
673 fprintf(fp, " </Lap>\n");
674 fprintf(fp, " <Notes>Created by auto-smoothing points</Notes>\n");
675 fprintf(fp, " </Activity>\n");
676 fprintf(fp, " </Activities>\n");
677 fprintf(fp, "</TrainingCenterDatabase>\n");
680 std::cout << summary.BriefReport() << "\n";