Geod.cpp
Go to the documentation of this file.
00001 /**
00002  * \file Geod.cpp
00003  * \brief Command line utility for geodesic calculations
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * http://charles.karney.info/geographic
00007  * and licensed under the LGPL.
00008  *
00009  * Compile with
00010  *
00011  *   g++ -g -O3 -I.. -o Geod Geod.cpp Geodesic.cpp DMS.cpp Constants.cpp
00012  *
00013  * See \ref geod for usage information.
00014  **********************************************************************/
00015 
00016 #include <string>
00017 #include <iostream>
00018 #include <iomanip>
00019 #include <sstream>
00020 #include <stdexcept>
00021 #include "GeographicLib/Geodesic.hpp"
00022 #include "GeographicLib/DMS.hpp"
00023 
00024 int usage(int retval) {
00025   ( retval ? std::cerr : std::cout ) <<
00026 "Usage: Geod [-l lat1 lon1 azi1 | -i] [-n] [-d] [-f] [-p prec] [-h]\n\
00027 $Id: Geod.cpp 6572 2009-03-01 22:41:48Z ckarney $\n\
00028 \n\
00029 Perform geodesic calculations.\n\
00030 \n\
00031 The shortest path between two points on the ellipsoid at (lat1, lon1)\n\
00032 and (lat2, lon2) is called the geodesic.  Its length is s12 and the\n\
00033 geodesic from point 1 to point 2 has azimuths azi1 and azi2 at the two\n\
00034 end points.\n\
00035 \n\
00036 Geod operates in one of three modes:\n\
00037 \n\
00038 (1) It accepts lines on the standard input containing \"lat1 lon1 azi1\n\
00039     s12\" and prints \"lat2 lon2 azi2\" on standard output.  This is the\n\
00040     direct geodesic calculation.\n\
00041 \n\
00042 (2) Command line arguments \"-l lat1 lon1 azi1\" specify a geodesic line.\n\
00043     Geod then accepts a sequence of s12 values (one per line) on\n\
00044     standard input and prints \"lat2 lon2 azi2\" for each.  This generates\n\
00045     a sequence of points on a single geodesic.\n\
00046 \n\
00047 (3) With the -i command line argument, Geod performs the inverse\n\
00048     geodesic calculation.  It reads lines containing \"lat1 lon1 lat2\n\
00049     lon2\" and prints the corresponding values of \"azi1 azi2 s12\".\n\
00050 \n\
00051 By default, the WGS84 ellipsoid is used.  With the -n option, it uses\n\
00052 the international ellipsoid (major radius 6378388 m, inverse flattening\n\
00053 297).\n\
00054 \n\
00055 Output of angles is as decimal degrees.  If -d is specified the output\n\
00056 is as degrees, minutes, seconds.  Input can be in either style.  d, ',\n\
00057 and \" are used to denote degrees, minutes, and seconds, with the least\n\
00058 significant designator optional.  By default, latitude precedes\n\
00059 longitude for each point; however on input either may be given first by\n\
00060 appending N or S to the latitude and E or W to the longitude.  s12 is\n\
00061 always given in meters.\n\
00062 \n\
00063 The output lines consist of the three quantities needs to complete the\n\
00064 specification of the geodesic.  With the -f option, each line of output\n\
00065 is a complete geodesic specification consisting of seven quantities\n\
00066 \n\
00067     lat1 lon1 azi1 lat2 lon2 azi2 s12\n\
00068 \n\
00069 -p prec (default 3) gives the precision of the output relative to 1m.\n\
00070 The minimum value of prec is 0 (1 m accuracy) and the maximum value is\n\
00071 10 (0.1 nm accuracy, but then the last digits are unreliable).\n\
00072 \n\
00073 -h prints this help.\n";
00074   return retval;
00075 }
00076 
00077 
00078 std::string LatLonString(double lat, double lon, int prec, bool dms) {
00079   using namespace GeographicLib;
00080   if (dms)
00081     return
00082       DMS::Encode(lat, prec + 5, DMS::LATITUDE) + " " +
00083       DMS::Encode(lon, prec + 5, DMS::LONGITUDE);
00084   else {
00085     std::ostringstream os;
00086     os << std::fixed << std::setprecision(prec + 5)
00087        << lat << " " << lon;
00088     return os.str();
00089   }
00090 }
00091 
00092 std::string AzimuthString(double azi, int prec, bool dms) {
00093   using namespace GeographicLib;
00094   if (dms)
00095     return DMS::Encode(azi, prec + 5, DMS::AZIMUTH);
00096   else {
00097     std::ostringstream os;
00098     os << std::fixed << std::setprecision(prec + 5)
00099        << azi;
00100     return os.str();
00101   }
00102 }
00103 
00104 double ReadAzimuth(const std::string& s) {
00105   using namespace GeographicLib;
00106   DMS::flag ind;
00107   double azi = DMS::Decode(s, ind);
00108   if (!(azi >= -180 && azi <= 360))
00109     throw std::out_of_range("Azimuth " + s + " not in range [-180,360]");
00110   if (azi >= 180) azi -= 360;
00111   if (ind == DMS::LATITUDE)
00112     throw std::out_of_range("Azimuth " + s +
00113                             " has a latitude hemisphere, N/S");
00114   return azi;
00115 }
00116 
00117 int main(int argc, char* argv[]) {
00118   bool linecalc = false, inverse = false, international = false,
00119     dms = false, full = false;
00120   double lat1, lon1, azi1, lat2, lon2, azi2, s12;
00121   int prec = 3;
00122 
00123   for (int m = 1; m < argc; ++m) {
00124     std::string arg = std::string(argv[m]);
00125     if (arg == "-i") {
00126       inverse = true;
00127       linecalc = false;
00128     } else if (arg == "-l") {
00129       inverse = false;
00130       linecalc = true;
00131       if (m + 3 >= argc) return usage(1);
00132       try {
00133         GeographicLib::DMS::DecodeLatLon(std::string(argv[m + 1]),
00134                                          std::string(argv[m + 2]),
00135                                          lat1, lon1);
00136         azi1 = ReadAzimuth(std::string(argv[m + 3]));
00137         m += 3;
00138       }
00139       catch (std::out_of_range& e) {
00140         std::cerr << "ERROR: " << e.what() << "\n";
00141         return usage(1);
00142       }
00143     } else if (arg == "-n")
00144       international = true;
00145     else if (arg == "-d")
00146       dms = true;
00147     else if (arg == "-f")
00148       full = true;
00149     else if (arg == "-p") {
00150       if (++m == argc) return usage(1);
00151       std::string a = std::string(argv[m]);
00152       std::istringstream str(a);
00153       if (!(str >> prec)) return usage(1);
00154     } else
00155       return usage(arg != "-h");
00156   }
00157 
00158   const GeographicLib::Geodesic internat(6378388.0, 297.0);
00159   const GeographicLib::Geodesic& geod = international ? internat :
00160     GeographicLib::Geodesic::WGS84;
00161   GeographicLib::GeodesicLine l;
00162   if (linecalc)
00163     l = geod.Line(lat1, lon1, azi1);
00164 
00165   // Max precision = 9: 1 nm in distance, 10^-14 deg (= 1.1 nm),
00166   // 10^-10 sec (= 3 nm).
00167   prec = std::min(10, std::max(0, prec));
00168   std::cout << std::fixed << std::setprecision(prec);
00169   std::string s;
00170   int retval = 0;
00171   while (std::getline(std::cin, s)) {
00172     try {
00173       std::istringstream str(s);
00174       if (linecalc) {
00175         if (!(str >> s12))
00176           throw std::out_of_range("Incomplete input: " + s);
00177         l.Position(s12, lat2, lon2, azi2);
00178         if (full)
00179           std::cout << LatLonString(lat1, lon1, prec, dms) << " " <<
00180             AzimuthString(azi1, prec, dms) << " ";
00181         std::cout << LatLonString(lat2, lon2, prec, dms) << " " <<
00182           AzimuthString(azi2, prec, dms);
00183         if (full)
00184           std::cout << " " << s12;
00185         std::cout << "\n";
00186       } else if (inverse) {
00187         std::string slat1, slon1, slat2, slon2;
00188         if (!(str >> slat1 >> slon1 >> slat2 >> slon2))
00189           throw std::out_of_range("Incomplete input: " + s);
00190         GeographicLib::DMS::DecodeLatLon(slat1, slon1, lat1, lon1);
00191         GeographicLib::DMS::DecodeLatLon(slat2, slon2, lat2, lon2);
00192         geod.Inverse(lat1, lon1, lat2, lon2, s12, azi1, azi2);
00193         if (full)
00194           std::cout << LatLonString(lat1, lon1, prec, dms) << " ";
00195         std::cout << AzimuthString(azi1, prec, dms) << " ";
00196         if (full)
00197           std::cout << LatLonString(lat2, lon2, prec, dms) << " ";
00198         std::cout << AzimuthString(azi2, prec, dms) << " " << s12 << "\n";
00199       } else {
00200         std::string slat1, slon1, sazi1;
00201         if (!(str >> slat1 >> slon1 >> sazi1 >> s12))
00202           throw std::out_of_range("Incomplete input: " + s);
00203         GeographicLib::DMS::DecodeLatLon(slat1, slon1, lat1, lon1);
00204         azi1 = ReadAzimuth(sazi1);
00205         geod.Direct(lat1, lon1, azi1, s12, lat2, lon2, azi2);
00206         if (full)
00207           std::cout << LatLonString(lat1, lon1, prec, dms) << " " <<
00208             AzimuthString(azi1, prec, dms) << " ";
00209         std::cout << LatLonString(lat2, lon2, prec, dms) << " " <<
00210           AzimuthString(azi2, prec, dms);
00211         if (full)
00212           std::cout << " " << s12;
00213         std::cout << "\n";
00214       }
00215     }
00216     catch (std::out_of_range& e) {
00217       // Write error message cout so output lines match input lines
00218       std::cout << "ERROR: " << e.what() << "\n";
00219       retval = 1;
00220     }
00221   }
00222   return retval;
00223 }