gpxls.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. #include <iostream>
  2. #include <cstring>
  3. #include <fstream>
  4. #include <vector>
  5. #include <limits>
  6. #include <iomanip>
  7. #include "Arguments.h"
  8. #include "XMLParser.h"
  9. #include "Algorithm.h"
  10. namespace gpxtools
  11. {
  12. const double INVALID = std::numeric_limits<double>::min();
  13. class GPXLs : public XMLParserHandler
  14. {
  15. public:
  16. // -- Constructor -----------------------------------------------------------
  17. GPXLs() :
  18. _arguments("gpxls [OPTION].. [FILE]..\nList summary or full information from GPX-files.\n", "gpxls v0.2", "List the contents of GPX-files"),
  19. _summary(_arguments, true, 's', "summary", "display summary information"),
  20. _full(_arguments, true, 'f', "full", "display full information with elevation (m), time and distance (m)"),
  21. _extend(_arguments, true, 'e', "extend", "extend the full display with the calculated bearing and speed (km/h)"),
  22. _xmlParser(this),
  23. _waypoints(),
  24. _routes(),
  25. _tracks()
  26. {
  27. }
  28. // -- Deconstructor----------------------------------------------------------
  29. virtual ~GPXLs()
  30. {
  31. }
  32. // -- Properties ------------------------------------------------------------
  33. // -- Parse arguments -------------------------------------------------------
  34. bool parseArguments(int argc, char *argv[])
  35. {
  36. std::vector<std::string> filenames;
  37. if (!_arguments.parse(argc,argv, filenames))
  38. {
  39. return false;
  40. }
  41. else if (!checkArguments())
  42. {
  43. return false;
  44. }
  45. else if (filenames.empty())
  46. {
  47. return _xmlParser.parse(std::cin);
  48. }
  49. else
  50. {
  51. for (auto filename = filenames.begin(); filename != filenames.end(); ++filename)
  52. {
  53. if (!parseFile(*filename)) return false;
  54. }
  55. }
  56. return true;
  57. }
  58. // -- Check arguments ---------------------------------------------------------
  59. bool checkArguments()
  60. {
  61. if (_summary.active() && _full.active())
  62. {
  63. _summary.active(false);
  64. }
  65. else if (!_summary.active() && !_full.active())
  66. {
  67. _summary.active(true);
  68. }
  69. return true;
  70. }
  71. // -- Parse a file ----------------------------------------------------------
  72. bool parseFile(const std::string &filename)
  73. {
  74. bool ok =false;
  75. std::ifstream file(filename);
  76. if (file.is_open())
  77. {
  78. ok = _xmlParser.parse(file);
  79. file.close();
  80. }
  81. else
  82. {
  83. std::cerr << "Unable to open: " << filename << std::endl;
  84. }
  85. return ok;
  86. }
  87. // -- Output the result -----------------------------------------------------
  88. void report()
  89. {
  90. std::cout << " Waypoints: " << _waypoints.size() << std::endl;
  91. if (_full.active() && !_waypoints.empty())
  92. {
  93. std::cout << " Waypoint Latitude Longitude Time Elevation" << std::fixed << std::endl;
  94. for (auto iter = _waypoints.begin(); iter != _waypoints.end(); ++iter)
  95. {
  96. std::cout << " ";
  97. report(iter->_name, 10);
  98. report(iter->_lat, 10, 5);
  99. report(iter->_lon, 10, 5);
  100. report(iter->_time, 24);
  101. report(getDouble(iter->_ele), 8, 3);
  102. std::cout << std::endl;
  103. }
  104. }
  105. std::cout << " Routes: " << _routes.size() << std::endl;
  106. for(auto iter = _routes.begin(); iter != _routes.end(); ++iter)
  107. {
  108. std::cout << " Route: '" << iter->_name
  109. << "' Points: " << iter->_points.size()
  110. << " Distance: " << std::fixed << std::setw(8) << std::setprecision(1) << getDistance(iter->_points)
  111. << std::endl;
  112. if (_full.active() && !iter->_points.empty())
  113. {
  114. std::cout << " Latitude Longitude" << std::fixed << std::endl;
  115. for (auto iter2 = iter->_points.begin(); iter2 != iter->_points.end(); ++iter2)
  116. {
  117. std::cout << " ";
  118. report(iter2->_lat, 10, 5);
  119. report(iter2->_lon, 10, 5);
  120. std::cout << std::endl;
  121. }
  122. }
  123. }
  124. std::cout << " Tracks: " << _tracks.size() << std::endl;
  125. for (auto iter = _tracks.begin(); iter != _tracks.end(); ++iter)
  126. {
  127. std::cout << " Track: '" << iter->_name << "' Segments: " << iter->_segments.size() << std::endl;
  128. int i = 1;
  129. for (auto iter2 = iter->_segments.begin(); iter2 != iter->_segments.end(); ++iter2, i++)
  130. {
  131. std::cout << " Segment: " << std::setw(2) << i
  132. << " Points: " << std::setw(4) << iter2->_points.size()
  133. << " Distance: " << std::fixed << std::setw(8) << std::setprecision(1) << getDistance(iter2->_points);
  134. if (!iter2->_minTime.empty() && !iter2->_maxTime.empty())
  135. {
  136. std::cout << " Bounds: " << iter2->_minTime << "..." << iter2->_maxTime;
  137. }
  138. std::cout << std::endl;
  139. if (_full.active() && !iter2->_points.empty())
  140. {
  141. bool extend = _extend.active();
  142. std::cout << " Latitude Longitude Time Elevation Distance";
  143. if (extend) std::cout << " Speed Bearing";
  144. std::cout << std::fixed << std::endl;
  145. double prevLat = INVALID;
  146. double prevLon = INVALID;
  147. time_t prevTime = 0;
  148. for (auto iter3 = iter2->_points.begin(); iter3 != iter2->_points.end(); ++iter3)
  149. {
  150. std::cout << " ";
  151. report(iter3->_lat, 10, 5);
  152. report(iter3->_lon, 10, 5);
  153. report(iter3->_time, 24);
  154. report(getDouble(iter3->_ele), 9, 1);
  155. double distance;
  156. double bearing;
  157. getDistanceAndBearing(prevLat, prevLon, iter3->_lat, iter3->_lon, distance, bearing);
  158. report(distance, 10, 1);
  159. if (extend)
  160. {
  161. report(getSpeed(distance, prevTime, iter3->_time), 7, 1);
  162. report(bearing, 7, 1);
  163. }
  164. std::cout << std::endl;
  165. }
  166. }
  167. }
  168. }
  169. }
  170. // -- Callbacks -------------------------------------------------------------
  171. virtual void startElement(const std::string &path, const std::string &, const Attributes &attributes)
  172. {
  173. if (path == "/gpx/wpt")
  174. {
  175. _waypoint.reset();
  176. _waypoint._lat = getDoubleAttribute(attributes, "lat");
  177. _waypoint._lon = getDoubleAttribute(attributes, "lon");
  178. }
  179. else if (path == "/gpx/rte")
  180. {
  181. _route.reset();
  182. }
  183. else if (path == "/gpx/rte/rtept")
  184. {
  185. _routepoint.reset();
  186. _routepoint._lat = getDoubleAttribute(attributes, "lat");
  187. _routepoint._lon = getDoubleAttribute(attributes, "lon");
  188. }
  189. else if (path == "/gpx/trk")
  190. {
  191. _track.reset();
  192. }
  193. else if (path == "/gpx/trk/trkseg")
  194. {
  195. _trackSegment.reset();
  196. }
  197. else if (path == "/gpx/trk/trkseg/trkpt")
  198. {
  199. _trackpoint.reset();
  200. _trackpoint._lat = getDoubleAttribute(attributes, "lat");
  201. _trackpoint._lon = getDoubleAttribute(attributes, "lon");
  202. }
  203. }
  204. virtual void text(const std::string &path, const std::string &text)
  205. {
  206. if (path == "/gpx/wpt/name")
  207. {
  208. _waypoint._name += text;
  209. }
  210. else if (path == "/gpx/wpt/ele")
  211. {
  212. _waypoint._ele += text;
  213. }
  214. else if (path == "/gpx/wpt/time")
  215. {
  216. _waypoint._time += text;
  217. }
  218. else if (path == "/gpx/rte/name")
  219. {
  220. _route._name += text;
  221. }
  222. else if (path == "/gpx/trk/name")
  223. {
  224. _track._name += text;
  225. }
  226. else if (path == "/gpx/trk/trkseg/trkpt/ele")
  227. {
  228. _trackpoint._ele += text;
  229. }
  230. else if (path == "/gpx/trk/trkseg/trkpt/time")
  231. {
  232. _trackpoint._time += text;
  233. }
  234. }
  235. virtual void endElement(const std::string &path, const std::string &)
  236. {
  237. if (path == "/gpx/wpt")
  238. {
  239. XMLParser::trim(_waypoint._name);
  240. XMLParser::trim(_waypoint._ele);
  241. XMLParser::trim(_waypoint._time);
  242. _waypoints.push_back(_waypoint);
  243. }
  244. else if (path == "/gpx/rte/rtept")
  245. {
  246. _route._points.push_back(_routepoint);
  247. }
  248. else if (path == "/gpx/rte")
  249. {
  250. XMLParser::trim(_route._name);
  251. _routes.push_back(_route);
  252. }
  253. else if (path == "/gpx/trk")
  254. {
  255. XMLParser::trim(_track._name);
  256. _tracks.push_back(_track);
  257. }
  258. else if (path == "/gpx/trk/trkseg")
  259. {
  260. _track._segments.push_back(_trackSegment);
  261. }
  262. else if (path == "/gpx/trk/trkseg/trkpt")
  263. {
  264. XMLParser::trim(_trackpoint._ele);
  265. XMLParser::trim(_trackpoint._time);
  266. if (_trackpoint._time.size() >= 18)
  267. {
  268. if (_trackSegment._minTime.empty() || _trackSegment._minTime > _trackpoint._time) _trackSegment._minTime = _trackpoint._time;
  269. if (_trackSegment._maxTime.empty() || _trackSegment._maxTime < _trackpoint._time) _trackSegment._maxTime = _trackpoint._time;
  270. }
  271. _trackSegment._points.push_back(_trackpoint);
  272. }
  273. }
  274. // -- Privates ----------------------------------------------------------------
  275. private:
  276. double getDouble(const std::string &value)
  277. {
  278. try
  279. {
  280. if (value.empty())
  281. {
  282. return INVALID;
  283. }
  284. else
  285. {
  286. return std::stod(value);
  287. }
  288. }
  289. catch (...)
  290. {
  291. return INVALID;
  292. }
  293. }
  294. double getDoubleAttribute(const Attributes &atts, const std::string &key)
  295. {
  296. auto iter = atts.find(key);
  297. return iter != atts.end() ? getDouble(iter->second) : INVALID;
  298. }
  299. double getDistance(double &prevLat, double &prevLon, double lat, double lon)
  300. {
  301. double distance = INVALID;
  302. if ((prevLat != INVALID) && (prevLon != INVALID) && (lat != INVALID) && (lon != INVALID))
  303. {
  304. distance = gpx::calcDistance(prevLat, prevLon, lat, lon);
  305. }
  306. prevLat = lat;
  307. prevLon = lon;
  308. return distance;
  309. }
  310. void getDistanceAndBearing(double &prevLat, double &prevLon, double lat, double lon, double &distance, double &bearing)
  311. {
  312. distance = INVALID;
  313. bearing = INVALID;
  314. if ((prevLat != INVALID) && (prevLon != INVALID) && (lat != INVALID) && (lon != INVALID))
  315. {
  316. distance = gpx::calcDistance(prevLat, prevLon, lat, lon);
  317. bearing = gpx::rad2deg(gpx::calcBearing(prevLat, prevLon, lat, lon)) + 180.0;
  318. }
  319. prevLat = lat;
  320. prevLon = lon;
  321. }
  322. double getSpeed(double distance, time_t &prevTime, const std::string &curTimeStr)
  323. {
  324. time_t curTime = 0;
  325. double speed = INVALID;
  326. if (!curTimeStr.empty())
  327. {
  328. std::string timeStr = curTimeStr;
  329. std::size_t index = timeStr.find(".");
  330. if (index != std::string::npos)
  331. {
  332. timeStr.erase(index, 4); // remove .000 millis
  333. }
  334. struct tm fields;
  335. memset(&fields, 0, sizeof(fields));
  336. if (strptime(timeStr.c_str(), "%FT%T%z", &fields) != nullptr)
  337. {
  338. curTime = mktime(&fields) - ::timezone;
  339. if ((prevTime != 0) && (prevTime != curTime) && (distance != INVALID))
  340. {
  341. speed = distance / (curTime - prevTime) * 3.6; // km/h
  342. }
  343. }
  344. }
  345. prevTime = curTime;
  346. return speed;
  347. }
  348. struct Trackpoint;
  349. double getDistance(const std::vector<Trackpoint> &points)
  350. {
  351. double distance = 0.0;
  352. double prevLat = INVALID;
  353. double prevLon = INVALID;
  354. for (auto point = points.begin(); point != points.end(); ++point)
  355. {
  356. distance += getDistance(prevLat, prevLon, point->_lat, point->_lon);
  357. }
  358. return distance;
  359. }
  360. struct Routepoint;
  361. double getDistance(const std::vector<Routepoint> &points)
  362. {
  363. double distance = 0.0;
  364. double prevLat = INVALID;
  365. double prevLon = INVALID;
  366. for (auto point = points.begin(); point != points.end(); ++point)
  367. {
  368. distance += getDistance(prevLat, prevLon, point->_lat, point->_lon);
  369. }
  370. return distance;
  371. }
  372. void report(double value, int width, int precision)
  373. {
  374. if (value == INVALID)
  375. {
  376. std::cout << std::setw(width) << ' ';
  377. }
  378. else
  379. {
  380. std::cout << std::setw(width) << std::setprecision(precision) << value;
  381. }
  382. std::cout << ' ';
  383. }
  384. void report(const std::string &value, size_t width)
  385. {
  386. std::cout << std::left;
  387. if (value.size() > width)
  388. {
  389. std::cout << value.substr(0, width-3) << "...";
  390. }
  391. else
  392. {
  393. std::cout << std::setw(width) << value;
  394. }
  395. std::cout << ' ';
  396. std::cout << std::right;
  397. }
  398. // -- Members ---------------------------------------------------------------
  399. arg::Arguments _arguments;
  400. arg::Argument _summary;
  401. arg::Argument _full;
  402. arg::Argument _extend;
  403. XMLParser _xmlParser;
  404. std::string _path;
  405. struct Waypoint
  406. {
  407. void reset()
  408. {
  409. _name.clear();
  410. _lat = INVALID;
  411. _lon = INVALID;
  412. _ele.clear();
  413. _time.clear();
  414. }
  415. std::string _name;
  416. double _lat;
  417. double _lon;
  418. std::string _ele;
  419. std::string _time;
  420. };
  421. Waypoint _waypoint;
  422. std::vector<Waypoint> _waypoints;
  423. struct Routepoint
  424. {
  425. void reset()
  426. {
  427. _lat = INVALID;
  428. _lon = INVALID;
  429. }
  430. double _lat;
  431. double _lon;
  432. };
  433. Routepoint _routepoint;
  434. struct Route
  435. {
  436. void reset()
  437. {
  438. _name.clear();
  439. _points.clear();
  440. }
  441. std::string _name;
  442. std::vector<Routepoint> _points;
  443. };
  444. Route _route;
  445. std::vector<Route> _routes;
  446. struct Trackpoint
  447. {
  448. void reset()
  449. {
  450. _lat = INVALID;
  451. _lon = INVALID;
  452. _ele.clear();
  453. _time.clear();
  454. }
  455. double _lat;
  456. double _lon;
  457. std::string _ele;
  458. std::string _time;
  459. };
  460. Trackpoint _trackpoint;
  461. struct TrackSegment
  462. {
  463. void reset()
  464. {
  465. _minTime.clear();
  466. _maxTime.clear();
  467. _points.clear();
  468. }
  469. std::string _minTime;
  470. std::string _maxTime;
  471. std::vector<Trackpoint> _points;
  472. };
  473. TrackSegment _trackSegment;
  474. struct Track
  475. {
  476. void reset()
  477. {
  478. _name.clear();
  479. _segments.clear();
  480. }
  481. std::string _name;
  482. std::vector<TrackSegment> _segments;
  483. };
  484. Track _track;
  485. std::vector<Track> _tracks;
  486. };
  487. }
  488. // -- Main program ------------------------------------------------------------
  489. int main(int argc, char *argv[])
  490. {
  491. gpxtools::GPXLs gpxLs;
  492. if (gpxLs.parseArguments(argc, argv))
  493. {
  494. gpxLs.report();
  495. return 0;
  496. }
  497. return 1;
  498. }