2015-12-05 22:49:18 +01:00
# include "./main.h"
# include "./location.h"
2015-12-08 08:33:39 +01:00
# include "resources/config.h"
2015-06-24 23:20:44 +02:00
# include <c++utilities/application/argumentparser.h>
# include <c++utilities/application/failure.h>
2017-01-27 21:28:01 +01:00
# include <c++utilities/conversion/stringconversion.h>
2016-06-14 22:55:41 +02:00
# include <c++utilities/io/catchiofailure.h>
2015-06-24 23:20:44 +02:00
# include <iostream>
# include <fstream>
# include <cstring>
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 {
2015-12-05 22:49:18 +01:00
SET_APPLICATION_INFO ;
2015-06-24 23:20:44 +02:00
ArgumentParser argparser ;
2016-06-14 00:47:07 +02:00
Argument convert ( " convert " , ' c ' , " Converts the given coordinate or location to the specified output form. " ) ;
2015-06-24 23:20:44 +02:00
convert . setRequiredValueCount ( 1 ) ;
convert . appendValueName ( " coordinate/location " ) ;
2016-06-14 00:47:07 +02:00
Argument distance ( " distance " , ' d ' , " Computes the approximate distance in meters between two locations. " ) ;
2015-06-24 23:20:44 +02:00
distance . setRequiredValueCount ( 2 ) ;
distance . appendValueName ( " location 1 " ) ;
distance . appendValueName ( " location 2 " ) ;
2016-06-14 00:47:07 +02:00
Argument trackLength ( " track-length " , ' t ' , " Computes the approximate length in meters of a track given by a file containing trackpoints separated by new lines. " ) ;
Argument fileArg ( " file " , ' f ' , " Specifies the file containing the track points " ) ;
fileArg . setRequiredValueCount ( 1 ) ;
fileArg . appendValueName ( " path " ) ;
fileArg . setRequired ( true ) ;
Argument circle ( " circle " , ' \0 ' , " If present the distance between the first and the last trackpoints will be added to the total track length. " ) ;
trackLength . setSubArguments ( { & fileArg , & circle } ) ;
2015-06-24 23:20:44 +02:00
2016-06-14 00:47:07 +02:00
Argument bearing ( " bearing " , ' b ' , " Computes the approximate initial bearing East of true North when traveling along the shortest path between the given locations. " ) ;
2015-06-24 23:20:44 +02:00
bearing . setRequiredValueCount ( 2 ) ;
bearing . appendValueName ( " location 1 " ) ;
bearing . appendValueName ( " location 2 " ) ;
2016-06-14 00:47:07 +02:00
Argument fbearing ( " final-bearing " , ' \0 ' , " Computes the approximate final bearing East of true North when traveling along the shortest path between the given locations. " ) ;
2015-06-24 23:20:44 +02:00
fbearing . setRequiredValueCount ( 2 ) ;
fbearing . appendValueName ( " location 1 " ) ;
fbearing . appendValueName ( " location 2 " ) ;
2016-06-14 00:47:07 +02:00
Argument midpoint ( " midpoint " , ' m ' , " Computes the approximate midpoint between the given locations. " ) ;
2015-06-24 23:20:44 +02:00
midpoint . setRequiredValueCount ( 2 ) ;
midpoint . appendValueName ( " location 1 " ) ;
midpoint . appendValueName ( " location 2 " ) ;
2016-06-14 00:47:07 +02:00
Argument destination ( " destination " , ' \0 ' , " Calculates destination point given distance and bearing from start point. " ) ;
2015-06-24 23:20:44 +02:00
destination . setRequiredValueCount ( 3 ) ;
destination . appendValueName ( " start " ) ;
destination . appendValueName ( " distance " ) ;
destination . appendValueName ( " bearing " ) ;
2016-06-14 00:47:07 +02:00
Argument gmapsLink ( " gmaps-link " , ' \0 ' , " Generates a Google Maps link for all locations given by a file containing locations separated by new lines. " ) ;
2015-06-24 23:20:44 +02:00
gmapsLink . setRequiredValueCount ( 1 ) ;
gmapsLink . appendValueName ( " path " ) ;
2016-06-14 00:47:07 +02:00
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). " ) ;
2015-06-24 23:20:44 +02:00
inputAngularMeasureArg . setRequiredValueCount ( 1 ) ;
inputAngularMeasureArg . appendValueName ( " angular measure " ) ;
inputAngularMeasureArg . setCombinable ( true ) ;
2016-06-14 00:47:07 +02:00
Argument outputFormForAnglesArg ( " output-angle-form " , ' o ' , " Use this option to specify the output form for angles (degrees, minutes, seconds or radians; default is degrees). " ) ;
2015-06-24 23:20:44 +02:00
outputFormForAnglesArg . setRequiredValueCount ( 1 ) ;
outputFormForAnglesArg . appendValueName ( " form " ) ;
outputFormForAnglesArg . setCombinable ( true ) ;
2016-06-14 00:47:07 +02:00
Argument inputSystemForLocationsArg ( " input-location-system " , ' \0 ' , " Use this option to specify the geographic system you use to provide locations (latitude&longitue or UTM-WGS84) . " ) ;
2015-06-24 23:20:44 +02:00
inputSystemForLocationsArg . setRequiredValueCount ( 1 ) ;
inputSystemForLocationsArg . appendValueName ( " system " ) ;
inputSystemForLocationsArg . setCombinable ( true ) ;
2016-06-14 00:47:07 +02:00
Argument outputSystemForLocationsArg ( " output-location-system " , ' \0 ' , " Use this option to specify which geographic system is used to display locations (latitude&longitue or UTM-WGS84) . " ) ;
2015-06-24 23:20:44 +02:00
outputSystemForLocationsArg . setRequiredValueCount ( 1 ) ;
outputSystemForLocationsArg . appendValueName ( " system " ) ;
outputSystemForLocationsArg . setCombinable ( true ) ;
HelpArgument help ( argparser ) ;
2016-06-14 00:47:07 +02:00
Argument version ( " version " , ' v ' , " Shows the version of this application. " ) ;
argparser . setMainArguments ( { & help , & convert , & distance , & trackLength , & bearing , & fbearing , & midpoint , & destination , & gmapsLink , & inputAngularMeasureArg , & outputFormForAnglesArg , & inputSystemForLocationsArg , & outputSystemForLocationsArg , & version } ) ;
2015-06-24 23:20:44 +02:00
argparser . parseArgs ( argc , argv ) ;
if ( inputAngularMeasureArg . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
const char * inputFormat = inputAngularMeasureArg . values ( ) . front ( ) ;
if ( ! strcmp ( inputFormat , " radian " ) ) {
2015-06-24 23:20:44 +02:00
inputAngularMeasure = Angle : : AngularMeasure : : Radian ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( inputFormat , " degree " ) ) {
2015-06-24 23:20:44 +02:00
inputAngularMeasure = Angle : : AngularMeasure : : Degree ;
} else {
2016-06-14 00:47:07 +02:00
cerr < < " Invalid angular measure given, see --help. " < < endl ;
2015-06-24 23:20:44 +02:00
return 0 ;
}
}
if ( outputFormForAnglesArg . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
const char * outputFormat = outputFormForAnglesArg . values ( ) . front ( ) ;
if ( ! strcmp ( outputFormat , " degrees " ) ) {
2015-06-24 23:20:44 +02:00
outputFormForAngles = Angle : : OutputForm : : Degrees ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( outputFormat , " minutes " ) ) {
2015-06-24 23:20:44 +02:00
outputFormForAngles = Angle : : OutputForm : : Minutes ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( outputFormat , " seconds " ) ) {
2015-06-24 23:20:44 +02:00
outputFormForAngles = Angle : : OutputForm : : Seconds ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( outputFormat , " radians " ) ) {
2015-06-24 23:20:44 +02:00
outputFormForAngles = Angle : : OutputForm : : Radians ;
} else {
2016-06-14 00:47:07 +02:00
cerr < < " Invalid output form for angles given, see --help. " < < endl ;
2015-06-24 23:20:44 +02:00
return 0 ;
}
}
if ( inputSystemForLocationsArg . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
const char * inputFormat = inputSystemForLocationsArg . values ( ) . front ( ) ;
if ( ! strcmp ( inputFormat , " latitude&longitue " ) ) {
2015-06-24 23:20:44 +02:00
inputSystemForLocations = SystemForLocations : : LatitudeLongitude ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( inputFormat , " UTM-WGS84 " ) ) {
2015-06-24 23:20:44 +02:00
inputSystemForLocations = SystemForLocations : : UTMWGS84 ;
} else {
2016-06-14 00:47:07 +02:00
cerr < < " Invalid geographic coordinate system given, see --help. " < < endl ;
2015-06-24 23:20:44 +02:00
return 0 ;
}
}
if ( outputSystemForLocationsArg . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
const char * outputSystem = outputSystemForLocationsArg . values ( ) . front ( ) ;
if ( ! strcmp ( outputSystem , " latitude&longitue " ) ) {
2015-06-24 23:20:44 +02:00
outputSystemForLocations = SystemForLocations : : LatitudeLongitude ;
2016-06-14 00:47:07 +02:00
} else if ( ! strcmp ( outputSystem , " UTM-WGS84 " ) ) {
2015-06-24 23:20:44 +02:00
outputSystemForLocations = SystemForLocations : : UTMWGS84 ;
} else {
2016-06-14 00:47:07 +02:00
cerr < < " Invalid geographic coordinate system given, see --help. " < < endl ;
2015-06-24 23:20:44 +02:00
return 0 ;
}
}
try {
if ( help . isPresent ( ) ) {
cout < < endl ;
printAngleFormatInfo ( cout ) ;
} else if ( version . isPresent ( ) ) {
2015-12-05 22:49:18 +01:00
cout < < APP_VERSION ;
2015-06-24 23:20:44 +02:00
} else if ( convert . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printConversion ( convert . values ( ) . front ( ) ) ;
2015-06-24 23:20:44 +02:00
} else if ( distance . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printDistance ( distance . values ( ) [ 0 ] , distance . values ( ) [ 1 ] ) ;
2015-06-24 23:20:44 +02:00
} else if ( trackLength . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printTrackLength ( fileArg . values ( ) . front ( ) , circle . isPresent ( ) ) ;
2015-06-24 23:20:44 +02:00
} else if ( bearing . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printBearing ( bearing . values ( ) [ 0 ] , bearing . values ( ) [ 1 ] ) ;
2015-06-24 23:20:44 +02:00
} else if ( fbearing . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printFinalBearing ( fbearing . values ( ) [ 0 ] , fbearing . values ( ) [ 1 ] ) ;
2015-06-24 23:20:44 +02:00
} else if ( midpoint . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printMidpoint ( midpoint . values ( ) [ 0 ] , midpoint . values ( ) [ 1 ] ) ;
2015-06-24 23:20:44 +02:00
} else if ( destination . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printDestination ( destination . values ( ) [ 0 ] , destination . values ( ) [ 1 ] , destination . values ( ) [ 2 ] ) ;
2015-06-24 23:20:44 +02:00
} else if ( gmapsLink . isPresent ( ) ) {
2016-06-14 00:47:07 +02:00
printMapsLink ( gmapsLink . values ( ) . front ( ) ) ;
2015-06-24 23:20:44 +02:00
} else {
2016-06-14 00:47:07 +02:00
cerr < < " No arguments given. See --help for available commands. " ;
2015-06-24 23:20:44 +02:00
}
2017-01-27 21:28:01 +01:00
} catch ( const ConversionException & ) {
cerr < < " The provided numbers couldn't be parsed correctly. " < < endl ;
cerr < < endl ;
printAngleFormatInfo ( cerr ) ;
2016-06-14 00:47:07 +02:00
} catch ( const Failure & ex ) {
2017-01-27 21:28:01 +01:00
cerr < < " The provided locations/coordinates couldn't be parsed correctly: " < < ex . what ( ) < < endl ;
2016-06-14 00:47:07 +02:00
cerr < < endl ;
printAngleFormatInfo ( cerr ) ;
2015-06-24 23:20:44 +02:00
}
2016-06-14 00:47:07 +02:00
} catch ( const Failure & ex ) {
cerr < < " Unable to parse arguments. " < < ex . what ( ) < < endl < < " See --help for available commands. " ;
2015-06-24 23:20:44 +02:00
}
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 < Location > locationsFromFile ( const string & path )
{
// prepare reading
fstream file ;
file . open ( path , ios_base : : in ) ;
if ( ! file )
2016-06-14 22:55:41 +02:00
IoUtilities : : throwIoFailure ( ( " Unable to open the file \" " + path + " \" . " ) . data ( ) ) ;
2015-06-24 23:20:44 +02:00
file . exceptions ( ios_base : : badbit ) ;
string line ;
vector < Location > 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 < < " \n Use 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 < Location > locations ( locationsFromFile ( filePath ) ) ;
printDistance ( Location : : trackLength ( locations , circle ) ) ;
cout < < " ( " < < locations . size ( ) < < " trackpoints) " ;
2016-06-14 22:55:41 +02:00
} catch ( . . . ) {
const char * what = : : IoUtilities : : catchIoFailure ( ) ;
cout < < " An IO failure occured when reading file from provided path: " < < what < < endl ;
2015-06-24 23:20:44 +02:00
}
}
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 ) ;
2017-01-27 21:28:01 +01:00
double distance = stringToNumber < double > ( distancestr ) ;
2015-06-24 23:20:44 +02:00
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 < Location > 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 < Location > : : 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. " ) ;
}
2016-06-14 22:55:41 +02:00
} catch ( . . . ) {
const char * what = : : IoUtilities : : catchIoFailure ( ) ;
cout < < " An IO failure occured when reading file from provided path: " < < what < < endl ;
2015-06-24 23:20:44 +02:00
}
}