123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- #include <cstring>
- #include <fstream>
- #include <iomanip>
- #include <iostream>
- #include <limits>
- #include <vector>
- #include "Algorithm.h"
- #include "Arguments.h"
- #include "XMLParser.h"
- namespace gpxtools
- {
- const double INVALID = std::numeric_limits<double>::min();
- class GPXLs : public XMLParserHandler
- {
- public:
- // -- Constructor -----------------------------------------------------------
- GPXLs() :
- _arguments("gpxls [OPTION].. [FILE]..\nList summary or full information from GPX-files.\n", "gpxls v0.2", "List the contents of GPX-files"),
- _summary(_arguments, true, 's', "summary", "display summary information"),
- _full(_arguments, true, 'f', "full", "display full information with elevation (m), time and distance (m)"),
- _extend(_arguments, true, 'e', "extend", "extend the full display with the calculated bearing and speed (km/h)"),
- _xmlParser(this),
- _waypoints(),
- _routes(),
- _tracks()
- {
- }
- // -- Deconstructor----------------------------------------------------------
- virtual ~GPXLs()
- {
- }
- // -- Properties ------------------------------------------------------------
- // -- Parse arguments -------------------------------------------------------
- bool parseArguments(int argc, char *argv[])
- {
- std::vector<std::string> filenames;
- if (!_arguments.parse(argc,argv, filenames))
- {
- return false;
- }
- else if (!checkArguments())
- {
- return false;
- }
- else if (filenames.empty())
- {
- return _xmlParser.parse(std::cin);
- }
- else
- {
- for (auto filename = filenames.begin(); filename != filenames.end(); ++filename)
- {
- if (!parseFile(*filename)) return false;
- }
- }
- return true;
- }
- // -- Check arguments ---------------------------------------------------------
- bool checkArguments()
- {
- if (_summary.active() && _full.active())
- {
- _summary.active(false);
- }
- else if (!_summary.active() && !_full.active())
- {
- _summary.active(true);
- }
- return true;
- }
- // -- Parse a file ----------------------------------------------------------
- bool parseFile(const std::string &filename)
- {
- bool ok =false;
- std::ifstream file(filename);
- if (file.is_open())
- {
- ok = _xmlParser.parse(file);
- file.close();
- }
- else
- {
- std::cerr << "Unable to open: " << filename << std::endl;
- }
- return ok;
- }
- // -- Output the result -----------------------------------------------------
- void report()
- {
- std::cout << " Waypoints: " << _waypoints.size() << std::endl;
- if (_full.active() && !_waypoints.empty())
- {
- std::cout << " Waypoint Latitude Longitude Time Elevation" << std::fixed << std::endl;
- for (auto iter = _waypoints.begin(); iter != _waypoints.end(); ++iter)
- {
- std::cout << " ";
- report(iter->_name, 10);
- report(iter->_lat, 10, 5);
- report(iter->_lon, 10, 5);
- report(iter->_time, 24);
- report(getDouble(iter->_ele), 8, 3);
- std::cout << std::endl;
- }
- }
- std::cout << " Routes: " << _routes.size() << std::endl;
- for(auto iter = _routes.begin(); iter != _routes.end(); ++iter)
- {
- std::cout << " Route: '" << iter->_name
- << "' Points: " << iter->_points.size()
- << " Distance: " << std::fixed << std::setw(8) << std::setprecision(1) << getDistance(iter->_points)
- << std::endl;
- if (_full.active() && !iter->_points.empty())
- {
- std::cout << " Latitude Longitude" << std::fixed << std::endl;
- for (auto iter2 = iter->_points.begin(); iter2 != iter->_points.end(); ++iter2)
- {
- std::cout << " ";
- report(iter2->_lat, 10, 5);
- report(iter2->_lon, 10, 5);
- std::cout << std::endl;
- }
- }
- }
- std::cout << " Tracks: " << _tracks.size() << std::endl;
- for (auto iter = _tracks.begin(); iter != _tracks.end(); ++iter)
- {
- std::cout << " Track: '" << iter->_name << "' Segments: " << iter->_segments.size() << std::endl;
- int i = 1;
- for (auto iter2 = iter->_segments.begin(); iter2 != iter->_segments.end(); ++iter2, i++)
- {
- std::cout << " Segment: " << std::setw(2) << i
- << " Points: " << std::setw(4) << iter2->_points.size()
- << " Distance: " << std::fixed << std::setw(8) << std::setprecision(1) << getDistance(iter2->_points);
- if (!iter2->_minTime.empty() && !iter2->_maxTime.empty())
- {
- std::cout << " Bounds: " << iter2->_minTime << "..." << iter2->_maxTime;
- }
- std::cout << std::endl;
- if (_full.active() && !iter2->_points.empty())
- {
- bool extend = _extend.active();
- std::cout << " Latitude Longitude Time Elevation Distance";
- if (extend) std::cout << " Speed Bearing";
- std::cout << std::fixed << std::endl;
- double prevLat = INVALID;
- double prevLon = INVALID;
- time_t prevTime = 0;
- for (auto iter3 = iter2->_points.begin(); iter3 != iter2->_points.end(); ++iter3)
- {
- std::cout << " ";
- report(iter3->_lat, 10, 5);
- report(iter3->_lon, 10, 5);
- report(iter3->_time, 24);
- report(getDouble(iter3->_ele), 9, 1);
- double distance;
- double bearing;
- getDistanceAndBearing(prevLat, prevLon, iter3->_lat, iter3->_lon, distance, bearing);
- report(distance, 10, 1);
- if (extend)
- {
- report(getSpeed(distance, prevTime, iter3->_time), 7, 1);
- report(bearing, 7, 1);
- }
- std::cout << std::endl;
- }
- }
- }
- }
- }
- // -- Callbacks -------------------------------------------------------------
- virtual void startElement(const std::string &path, const std::string &, const Attributes &attributes)
- {
- if (path == "/gpx/wpt")
- {
- _waypoint.reset();
- _waypoint._lat = getDoubleAttribute(attributes, "lat");
- _waypoint._lon = getDoubleAttribute(attributes, "lon");
- }
- else if (path == "/gpx/rte")
- {
- _route.reset();
- }
- else if (path == "/gpx/rte/rtept")
- {
- _routepoint.reset();
- _routepoint._lat = getDoubleAttribute(attributes, "lat");
- _routepoint._lon = getDoubleAttribute(attributes, "lon");
- }
- else if (path == "/gpx/trk")
- {
- _track.reset();
- }
- else if (path == "/gpx/trk/trkseg")
- {
- _trackSegment.reset();
- }
- else if (path == "/gpx/trk/trkseg/trkpt")
- {
- _trackpoint.reset();
- _trackpoint._lat = getDoubleAttribute(attributes, "lat");
- _trackpoint._lon = getDoubleAttribute(attributes, "lon");
- }
- }
- virtual void text(const std::string &path, const std::string &text)
- {
- if (path == "/gpx/wpt/name")
- {
- _waypoint._name += text;
- }
- else if (path == "/gpx/wpt/ele")
- {
- _waypoint._ele += text;
- }
- else if (path == "/gpx/wpt/time")
- {
- _waypoint._time += text;
- }
- else if (path == "/gpx/rte/name")
- {
- _route._name += text;
- }
- else if (path == "/gpx/trk/name")
- {
- _track._name += text;
- }
- else if (path == "/gpx/trk/trkseg/trkpt/ele")
- {
- _trackpoint._ele += text;
- }
- else if (path == "/gpx/trk/trkseg/trkpt/time")
- {
- _trackpoint._time += text;
- }
- }
- virtual void endElement(const std::string &path, const std::string &)
- {
- if (path == "/gpx/wpt")
- {
- XMLParser::trim(_waypoint._name);
- XMLParser::trim(_waypoint._ele);
- XMLParser::trim(_waypoint._time);
- _waypoints.push_back(_waypoint);
- }
- else if (path == "/gpx/rte/rtept")
- {
- _route._points.push_back(_routepoint);
- }
- else if (path == "/gpx/rte")
- {
- XMLParser::trim(_route._name);
- _routes.push_back(_route);
- }
- else if (path == "/gpx/trk")
- {
- XMLParser::trim(_track._name);
- _tracks.push_back(_track);
- }
- else if (path == "/gpx/trk/trkseg")
- {
- _track._segments.push_back(_trackSegment);
- }
- else if (path == "/gpx/trk/trkseg/trkpt")
- {
- XMLParser::trim(_trackpoint._ele);
- XMLParser::trim(_trackpoint._time);
- if (_trackpoint._time.size() >= 18)
- {
- if (_trackSegment._minTime.empty() || _trackSegment._minTime > _trackpoint._time) _trackSegment._minTime = _trackpoint._time;
- if (_trackSegment._maxTime.empty() || _trackSegment._maxTime < _trackpoint._time) _trackSegment._maxTime = _trackpoint._time;
- }
- _trackSegment._points.push_back(_trackpoint);
- }
- }
- // -- Privates ----------------------------------------------------------------
- private:
- double getDouble(const std::string &value)
- {
- try
- {
- if (value.empty())
- {
- return INVALID;
- }
- else
- {
- return std::stod(value);
- }
- }
- catch (...)
- {
- return INVALID;
- }
- }
- double getDoubleAttribute(const Attributes &atts, const std::string &key)
- {
- auto iter = atts.find(key);
- return iter != atts.end() ? getDouble(iter->second) : INVALID;
- }
- double getDistance(double &prevLat, double &prevLon, double lat, double lon)
- {
- double distance = INVALID;
- if ((prevLat != INVALID) && (prevLon != INVALID) && (lat != INVALID) && (lon != INVALID))
- {
- distance = gpx::calcDistance(prevLat, prevLon, lat, lon);
- }
- prevLat = lat;
- prevLon = lon;
- return distance;
- }
- void getDistanceAndBearing(double &prevLat, double &prevLon, double lat, double lon, double &distance, double &bearing)
- {
- distance = INVALID;
- bearing = INVALID;
- if ((prevLat != INVALID) && (prevLon != INVALID) && (lat != INVALID) && (lon != INVALID))
- {
- distance = gpx::calcDistance(prevLat, prevLon, lat, lon);
- bearing = gpx::calcBearingInDeg(prevLat, prevLon, lat, lon);
- }
- prevLat = lat;
- prevLon = lon;
- }
- double getSpeed(double distance, time_t &prevTime, const std::string &curTimeStr)
- {
- time_t curTime = 0;
- double speed = INVALID;
- if (!curTimeStr.empty())
- {
- std::string timeStr = curTimeStr;
- std::size_t index = timeStr.find(".");
- if (index != std::string::npos)
- {
- timeStr.erase(index, 4); // remove .000 millis
- }
- struct tm fields;
- memset(&fields, 0, sizeof(fields));
- if (strptime(timeStr.c_str(), "%FT%T%z", &fields) != nullptr)
- {
- curTime = mktime(&fields) - ::timezone;
- if ((prevTime != 0) && (prevTime != curTime) && (distance != INVALID))
- {
- speed = distance / (curTime - prevTime) * 3.6; // km/h
- }
- }
- }
- prevTime = curTime;
- return speed;
- }
- struct Trackpoint;
- double getDistance(const std::vector<Trackpoint> &points)
- {
- double distance = 0.0;
- double prevLat = INVALID;
- double prevLon = INVALID;
- for (auto point = points.begin(); point != points.end(); ++point)
- {
- distance += getDistance(prevLat, prevLon, point->_lat, point->_lon);
- }
- return distance;
- }
- struct Routepoint;
- double getDistance(const std::vector<Routepoint> &points)
- {
- double distance = 0.0;
- double prevLat = INVALID;
- double prevLon = INVALID;
- for (auto point = points.begin(); point != points.end(); ++point)
- {
- distance += getDistance(prevLat, prevLon, point->_lat, point->_lon);
- }
- return distance;
- }
- void report(double value, int width, int precision)
- {
- if (value == INVALID)
- {
- std::cout << std::setw(width) << ' ';
- }
- else
- {
- std::cout << std::setw(width) << std::setprecision(precision) << value;
- }
- std::cout << ' ';
- }
- void report(const std::string &value, size_t width)
- {
- std::cout << std::left;
- if (value.size() > width)
- {
- std::cout << value.substr(0, width-3) << "...";
- }
- else
- {
- std::cout << std::setw(width) << value;
- }
- std::cout << ' ';
- std::cout << std::right;
- }
- // -- Members ---------------------------------------------------------------
- arg::Arguments _arguments;
- arg::Argument _summary;
- arg::Argument _full;
- arg::Argument _extend;
- XMLParser _xmlParser;
- std::string _path;
- struct Waypoint
- {
- void reset()
- {
- _name.clear();
- _lat = INVALID;
- _lon = INVALID;
- _ele.clear();
- _time.clear();
- }
- std::string _name;
- double _lat;
- double _lon;
- std::string _ele;
- std::string _time;
- };
- Waypoint _waypoint;
- std::vector<Waypoint> _waypoints;
- struct Routepoint
- {
- void reset()
- {
- _lat = INVALID;
- _lon = INVALID;
- }
- double _lat;
- double _lon;
- };
- Routepoint _routepoint;
- struct Route
- {
- void reset()
- {
- _name.clear();
- _points.clear();
- }
- std::string _name;
- std::vector<Routepoint> _points;
- };
- Route _route;
- std::vector<Route> _routes;
- struct Trackpoint
- {
- void reset()
- {
- _lat = INVALID;
- _lon = INVALID;
- _ele.clear();
- _time.clear();
- }
- double _lat;
- double _lon;
- std::string _ele;
- std::string _time;
- };
- Trackpoint _trackpoint;
- struct TrackSegment
- {
- void reset()
- {
- _minTime.clear();
- _maxTime.clear();
- _points.clear();
- }
- std::string _minTime;
- std::string _maxTime;
- std::vector<Trackpoint> _points;
- };
- TrackSegment _trackSegment;
- struct Track
- {
- void reset()
- {
- _name.clear();
- _segments.clear();
- }
- std::string _name;
- std::vector<TrackSegment> _segments;
- };
- Track _track;
- std::vector<Track> _tracks;
- };
- }
- // -- Main program ------------------------------------------------------------
- int main(int argc, char *argv[])
- {
- gpxtools::GPXLs gpxLs;
- if (gpxLs.parseArguments(argc, argv))
- {
- gpxLs.report();
- return 0;
- }
- return 1;
- }
|