#include "main.h" #include "location.h" #include "utils.h" #include #include #include #include #include using namespace std; using namespace ConversionUtilities; using namespace ApplicationUtilities; Angle::AngularMeasure inputAngularMeasure = Angle::AngularMeasure::Degree; Angle::OutputForm outputFormForAngles = Angle::OutputForm::Degrees; SystemForLocations inputSystemForLocations = SystemForLocations::LatitudeLongitude; SystemForLocations outputSystemForLocations = SystemForLocations::LatitudeLongitude; int main(int argc, char *argv[]) { try { ArgumentParser argparser; Argument convert("convert", "c", "Converts the given coordinate or location to the specified output form."); convert.setRequiredValueCount(1); convert.appendValueName("coordinate/location"); Argument distance("distance", "d", "Computes the approximate distance in meters between two locations."); distance.setRequiredValueCount(2); distance.appendValueName("location 1"); distance.appendValueName("location 2"); Argument trackLength("track-length", "t", "Computes the approximate length in meters of a track given by a file containing trackpoints separated by new lines."); trackLength.setRequiredValueCount(1); trackLength.appendValueName("path"); Argument circle("circle", "", "If present the distance between the first and the last trackpoints will be added to the total track length."); trackLength.setSecondaryArguments({&circle}); Argument bearing("bearing", "b", "Computes the approximate initial bearing East of true North when traveling along the shortest path between the given locations."); bearing.setRequiredValueCount(2); bearing.appendValueName("location 1"); bearing.appendValueName("location 2"); Argument fbearing("final-bearing", string(), "Computes the approximate final bearing East of true North when traveling along the shortest path between the given locations."); fbearing.setRequiredValueCount(2); fbearing.appendValueName("location 1"); fbearing.appendValueName("location 2"); Argument midpoint("midpoint", "m", "Computes the approximate midpoint between the given locations."); midpoint.setRequiredValueCount(2); midpoint.appendValueName("location 1"); midpoint.appendValueName("location 2"); Argument destination("destination", string(), "Calculates destination point given distance and bearing from start point."); destination.setRequiredValueCount(3); destination.appendValueName("start"); destination.appendValueName("distance"); destination.appendValueName("bearing"); Argument gmapsLink("gmaps-link", "", "Generates a Google Maps link for all locations given by a file containing locations separated by new lines."); gmapsLink.setRequiredValueCount(1); gmapsLink.appendValueName("path"); Argument inputAngularMeasureArg("input-angular-measure", "i", "Use this option to specify the angular measure you use to provide angles (degree or radian; default is degree)."); inputAngularMeasureArg.setRequiredValueCount(1); inputAngularMeasureArg.appendValueName("angular measure"); inputAngularMeasureArg.setCombinable(true); Argument outputFormForAnglesArg("output-angle-form", "o", "Use this option to specify the output form for angles (degrees, minutes, seconds or radians; default is degrees)."); outputFormForAnglesArg.setRequiredValueCount(1); outputFormForAnglesArg.appendValueName("form"); outputFormForAnglesArg.setCombinable(true); Argument inputSystemForLocationsArg("input-location-system", string(), "Use this option to specify the geographic system you use to provide locations (latitude&longitue or UTM-WGS84)."); inputSystemForLocationsArg.setRequiredValueCount(1); inputSystemForLocationsArg.appendValueName("system"); inputSystemForLocationsArg.setCombinable(true); Argument outputSystemForLocationsArg("output-location-system", string(), "Use this option to specify which geographic system is used to display locations (latitude&longitue or UTM-WGS84)."); outputSystemForLocationsArg.setRequiredValueCount(1); outputSystemForLocationsArg.appendValueName("system"); outputSystemForLocationsArg.setCombinable(true); HelpArgument help(argparser); Argument version("version", "v", "Shows the version of this application."); argparser.setMainArguments({&convert, &distance, &trackLength, &bearing, &fbearing, &midpoint, &destination, &gmapsLink, &help, &version, &inputAngularMeasureArg, &outputFormForAnglesArg, &inputSystemForLocationsArg, &outputSystemForLocationsArg}); argparser.parseArgs(argc, argv); if(inputAngularMeasureArg.isPresent()) { if(inputAngularMeasureArg.value(0) == "radian") { inputAngularMeasure = Angle::AngularMeasure::Radian; } else if(inputAngularMeasureArg.value(0) == "degree") { inputAngularMeasure = Angle::AngularMeasure::Degree; } else { cout << "Invalid angular measure given, see --help." << endl; return 0; } } if(outputFormForAnglesArg.isPresent()) { if(outputFormForAnglesArg.value(0) == "degrees") { outputFormForAngles = Angle::OutputForm::Degrees; } else if(outputFormForAnglesArg.value(0) == "minutes") { outputFormForAngles = Angle::OutputForm::Minutes; } else if(outputFormForAnglesArg.value(0) == "seconds") { outputFormForAngles = Angle::OutputForm::Seconds; } else if(outputFormForAnglesArg.value(0) == "radians") { outputFormForAngles = Angle::OutputForm::Radians; } else { cout << "Invalid output form for angles given, see --help." << endl; return 0; } } if(inputSystemForLocationsArg.isPresent()) { if(inputSystemForLocationsArg.value(0) == "latitude&longitue") { inputSystemForLocations = SystemForLocations::LatitudeLongitude; } else if(inputSystemForLocationsArg.value(0) == "UTM-WGS84") { inputSystemForLocations = SystemForLocations::UTMWGS84; } else { cout << "Invalid geographic coordinate system given, see --help." << endl; return 0; } } if(outputSystemForLocationsArg.isPresent()) { if(outputSystemForLocationsArg.value(0) == "latitude&longitue") { outputSystemForLocations = SystemForLocations::LatitudeLongitude; } else if(outputSystemForLocationsArg.value(0) == "UTM-WGS84") { outputSystemForLocations = SystemForLocations::UTMWGS84; } else { cout << "Invalid geographic coordinate system given, see --help." << endl; return 0; } } try { if(help.isPresent()) { cout << endl; printAngleFormatInfo(cout); } else if(version.isPresent()) { cout << "1.0.0"; } else if(convert.isPresent()) { printConversion(convert.value(0)); } else if(distance.isPresent()) { printDistance(distance.value(0), distance.value(1)); } else if(trackLength.isPresent()) { printTrackLength(trackLength.value(0), circle.isPresent()); } else if(bearing.isPresent()) { printBearing(bearing.value(0), bearing.value(1)); } else if(fbearing.isPresent()) { printFinalBearing(fbearing.value(0), fbearing.value(1)); } else if(midpoint.isPresent()) { printMidpoint(midpoint.value(0), midpoint.value(1)); } else if(destination.isPresent()) { printDestination(destination.value(0), destination.value(1), destination.value(2)); } else if(gmapsLink.isPresent()) { printMapsLink(gmapsLink.value(0)); } else { cout << "No arguments given. See --help for available commands."; } } catch(Failure &ex) { cout << "The provided locations/coordinates couldn't be parsed correctly. " << ex.what() << endl; cout << endl; printAngleFormatInfo(cout); } } catch(Failure &ex) { cout << "Unable to parse arguments. " << ex.what() << endl << "See --help for available commands."; } cout << endl; return 0; } Location locationFromString(const string &userInput) { switch(inputSystemForLocations) { case SystemForLocations::UTMWGS84: { Location l; l.setValueByProvidedUtmWgs4Coordinates(userInput); return l; } default: return Location(userInput, inputAngularMeasure); } } vector locationsFromFile(const string &path) { // prepare reading fstream file; file.open(path, ios_base::in); if(!file) throw ios_base::failure("Unable to open the file \"" + path + "\"."); file.exceptions(ios_base::badbit); string line; vector locations; while(getline(file, line)) { if(line.empty() || line.at(0) == '#') continue; // skip empty lines and comments locations.push_back(locationFromString(line)); } return locations; } void printAngleFormatInfo(ostream &os) { os << "To provide a location/trackpoint, use the following form:\n"; os << "latitude,longitude\n"; os << "\nUse one of the following forms to specify angles, if you use --input-angle-measure degree:\n"; os << "[+-]DDD.DDDDD\n"; os << "[+-]DDD:MM.MMMMM\n"; os << "[+-]DDD:MM:SS.SSSSS\n"; os << "D indicates degrees, M indicates minutes of arc, and S indicates seconds of arc (1 minute = 1/60th of a degree, 1 second = 1/3600th of a degree).\n"; os << "You can use the following form where R indicates radians, if you use --input-angle-measure radian:\n"; os << "[+-]RRR.RRRRR"; } void printConversion(const string &coordinates) { if(coordinates.find(',') == string::npos && coordinates.find('N') == string::npos && coordinates.find('E') == string::npos) cout << Angle(coordinates, inputAngularMeasure).toString(outputFormForAngles); else printLocation(locationFromString(coordinates)); } void printDistance(const std::string &locationstr1, const std::string &locationstr2) { printDistance(locationFromString(locationstr1) .distanceTo(locationFromString(locationstr2))); } void printDistance(double distance) { if(distance > 1000) cout << (distance / 1000.0) << " km"; else cout << distance << " m"; } void printTrackLength(const string &filePath, bool circle) { try { vector locations(locationsFromFile(filePath)); printDistance(Location::trackLength(locations, circle)); cout << " (" << locations.size() << " trackpoints)"; } catch(ios_base::failure &ex) { cout << "An IO failure occured when reading file from provided path: " << ex.what() << endl; } } void printBearing(const string &locationstr1, const string &locationstr2) { cout << locationFromString(locationstr1).initialBearingTo( locationFromString(locationstr2)) .toString(outputFormForAngles) << endl; } void printFinalBearing(const string &locationstr1, const string &locationstr2) { cout << locationFromString(locationstr1).finalBearingTo( locationFromString(locationstr2)) .toString(outputFormForAngles) << endl; } void printMidpoint(const string &locationstr1, const string &locationstr2) { printLocation(Location::midpoint( locationFromString(locationstr1), locationFromString(locationstr2))); } void printDestination(const string &locationstr, const string &distancestr, const string &bearingstr) { Location start = locationFromString(locationstr); double distance = numberFromString(distancestr); Angle bearing(bearingstr, inputAngularMeasure); printLocation(start.destination(distance, bearing)); } void printLocation(const Location &location) { switch(outputSystemForLocations) { case SystemForLocations::LatitudeLongitude: cout << location.toString(outputFormForAngles); break; case SystemForLocations::UTMWGS84: cout << location.toUtmWgs4String(); break; } } void printMapsLink(const string &filePath) { try { vector locations(locationsFromFile(filePath)); if(locations.size() > 0) { cout << "https://maps.google.de/maps?saddr="; const Angle::OutputForm outputForm = Angle::OutputForm::Degrees; cout << locations.front().toString(outputForm); if(locations.size() > 1) { cout << "&daddr="; cout << locations.at(1).toString(outputForm); } if(locations.size() > 2) { for(vector::const_iterator i = locations.cbegin() + 2, end = locations.cend(); i != end; ++i) { cout << "+to:"; cout << i->toString(outputForm); } } cout << "&mra=mi&mrsp=2&sz=16&z=16"; } else { throw Failure("At least one location is required to generate a link."); } } catch(ios_base::failure &ex) { cout << "An IO failure occured when reading file from provided path: " << ex.what() << endl; } }