GeographicLib  1.38
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RhumbSolve.cpp
Go to the documentation of this file.
1 /**
2  * \file RhumbSolve.cpp
3  * \brief Command line utility for rhumb line calculations
4  *
5  * Copyright (c) Charles Karney (2014) <charles@karney.com> and licensed
6  * under the MIT/X11 License. For more information, see
7  * http://geographiclib.sourceforge.net/
8  **********************************************************************/
9 
10 #include <iostream>
11 #include <sstream>
12 #include <string>
13 #include <sstream>
14 #include <fstream>
15 #include <cmath>
16 #include <limits>
17 #include <GeographicLib/Rhumb.hpp>
18 #include <GeographicLib/DMS.hpp>
20 
21 #if defined(_MSC_VER)
22 // Squelch warnings about constant conditional expressions and potentially
23 // uninitialized local variables
24 # pragma warning (disable: 4127 4701)
25 #endif
26 
27 #include "RhumbSolve.usage"
28 
29 using namespace GeographicLib;
30 typedef Math::real real;
31 
32 std::string LatLonString(real lat, real lon, int prec, bool dms, char dmssep) {
33  return dms ?
34  DMS::Encode(lat, prec + 5, DMS::LATITUDE, dmssep) + " " +
35  DMS::Encode(lon, prec + 5, DMS::LONGITUDE, dmssep) :
36  DMS::Encode(lat, prec + 5, DMS::NUMBER) + " " +
37  DMS::Encode(lon, prec + 5, DMS::NUMBER);
38 }
39 
40 std::string AzimuthString(real azi, int prec, bool dms, char dmssep) {
41  return dms ? DMS::Encode(azi, prec + 5, DMS::AZIMUTH, dmssep) :
42  DMS::Encode(azi >= 180 ? azi - 360 : azi, prec + 5, DMS::NUMBER);
43 }
44 
45 int main(int argc, char* argv[]) {
46  try {
48  bool linecalc = false, inverse = false, dms = false, exact = true;
49  real
50  a = Constants::WGS84_a(),
51  f = Constants::WGS84_f();
52  real lat1, lon1, azi12 = Math::NaN(), lat2, lon2, s12;
53  int prec = 3;
54  std::string istring, ifile, ofile, cdelim;
55  char lsep = ';', dmssep = char(0);
56 
57  for (int m = 1; m < argc; ++m) {
58  std::string arg(argv[m]);
59  if (arg == "-i") {
60  inverse = true;
61  linecalc = false;
62  } else if (arg == "-l") {
63  inverse = false;
64  linecalc = true;
65  if (m + 3 >= argc) return usage(1, true);
66  try {
67  DMS::DecodeLatLon(std::string(argv[m + 1]), std::string(argv[m + 2]),
68  lat1, lon1);
69  azi12 = DMS::DecodeAzimuth(std::string(argv[m + 3]));
70  }
71  catch (const std::exception& e) {
72  std::cerr << "Error decoding arguments of -l: " << e.what() << "\n";
73  return 1;
74  }
75  m += 3;
76  } else if (arg == "-e") {
77  if (m + 2 >= argc) return usage(1, true);
78  try {
79  a = Utility::num<real>(std::string(argv[m + 1]));
80  f = Utility::fract<real>(std::string(argv[m + 2]));
81  }
82  catch (const std::exception& e) {
83  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
84  return 1;
85  }
86  m += 2;
87  }
88  else if (arg == "-d") {
89  dms = true;
90  dmssep = '\0';
91  } else if (arg == "-:") {
92  dms = true;
93  dmssep = ':';
94  } else if (arg == "-p") {
95  if (++m == argc) return usage(1, true);
96  try {
97  prec = Utility::num<int>(std::string(argv[m]));
98  }
99  catch (const std::exception&) {
100  std::cerr << "Precision " << argv[m] << " is not a number\n";
101  return 1;
102  }
103  } else if (arg == "-s")
104  exact = false;
105  else if (arg == "--input-string") {
106  if (++m == argc) return usage(1, true);
107  istring = argv[m];
108  } else if (arg == "--input-file") {
109  if (++m == argc) return usage(1, true);
110  ifile = argv[m];
111  } else if (arg == "--output-file") {
112  if (++m == argc) return usage(1, true);
113  ofile = argv[m];
114  } else if (arg == "--line-separator") {
115  if (++m == argc) return usage(1, true);
116  if (std::string(argv[m]).size() != 1) {
117  std::cerr << "Line separator must be a single character\n";
118  return 1;
119  }
120  lsep = argv[m][0];
121  } else if (arg == "--comment-delimiter") {
122  if (++m == argc) return usage(1, true);
123  cdelim = argv[m];
124  } else if (arg == "--version") {
125  std::cout
126  << argv[0] << ": GeographicLib version "
127  << GEOGRAPHICLIB_VERSION_STRING << "\n";
128  return 0;
129  } else
130  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
131  }
132 
133  if (!ifile.empty() && !istring.empty()) {
134  std::cerr << "Cannot specify --input-string and --input-file together\n";
135  return 1;
136  }
137  if (ifile == "-") ifile.clear();
138  std::ifstream infile;
139  std::istringstream instring;
140  if (!ifile.empty()) {
141  infile.open(ifile.c_str());
142  if (!infile.is_open()) {
143  std::cerr << "Cannot open " << ifile << " for reading\n";
144  return 1;
145  }
146  } else if (!istring.empty()) {
147  std::string::size_type m = 0;
148  while (true) {
149  m = istring.find(lsep, m);
150  if (m == std::string::npos)
151  break;
152  istring[m] = '\n';
153  }
154  instring.str(istring);
155  }
156  std::istream* input = !ifile.empty() ? &infile :
157  (!istring.empty() ? &instring : &std::cin);
158 
159  std::ofstream outfile;
160  if (ofile == "-") ofile.clear();
161  if (!ofile.empty()) {
162  outfile.open(ofile.c_str());
163  if (!outfile.is_open()) {
164  std::cerr << "Cannot open " << ofile << " for writing\n";
165  return 1;
166  }
167  }
168  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
169 
170  const Rhumb rh(a, f, exact);
171  // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
172  // 10^-11 sec (= 0.3 nm).
173  prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
174  int retval = 0;
175  std::string s;
176  if (linecalc) {
177  const RhumbLine rhl(rh.Line(lat1, lon1, azi12));
178  while (std::getline(*input, s)) {
179  try {
180  std::istringstream str(s);
181  if (!(str >> s12))
182  throw GeographicErr("Incomplete input: " + s);
183  std::string strc;
184  if (str >> strc)
185  throw GeographicErr("Extraneous input: " + strc);
186  rhl.Position(s12, lat2, lon2);
187  *output << LatLonString(lat2, lon2, prec, dms, dmssep) << "\n";
188  }
189  catch (const std::exception& e) {
190  // Write error message cout so output lines match input lines
191  *output << "ERROR: " << e.what() << "\n";
192  retval = 1;
193  }
194  }
195  } else if (inverse) {
196  while (std::getline(*input, s)) {
197  try {
198  std::istringstream str(s);
199  std::string slat1, slon1, slat2, slon2;
200  if (!(str >> slat1 >> slon1 >> slat2 >> slon2))
201  throw GeographicErr("Incomplete input: " + s);
202  std::string strc;
203  if (str >> strc)
204  throw GeographicErr("Extraneous input: " + strc);
205  DMS::DecodeLatLon(slat1, slon1, lat1, lon1);
206  DMS::DecodeLatLon(slat2, slon2, lat2, lon2);
207  rh.Inverse(lat1, lon1, lat2, lon2, s12, azi12);
208  *output << AzimuthString(azi12, prec, dms, dmssep) << " "
209  << Utility::str(s12, prec) << "\n";
210  }
211  catch (const std::exception& e) {
212  // Write error message cout so output lines match input lines
213  *output << "ERROR: " << e.what() << "\n";
214  retval = 1;
215  }
216  }
217  } else {
218  while (std::getline(*input, s)) {
219  try {
220  std::istringstream str(s);
221  std::string slat1, slon1, sazi;
222  if (!(str >> slat1 >> slon1 >> sazi >> s12))
223  throw GeographicErr("Incomplete input: " + s);
224  std::string strc;
225  if (str >> strc)
226  throw GeographicErr("Extraneous input: " + strc);
227  DMS::DecodeLatLon(slat1, slon1, lat1, lon1);
228  azi12 = DMS::DecodeAzimuth(sazi);
229  rh.Direct(lat1, lon1, azi12, s12, lat2, lon2);
230  *output << LatLonString(lat2, lon2, prec, dms, dmssep) << "\n";
231  }
232  catch (const std::exception& e) {
233  // Write error message cout so output lines match input lines
234  *output << "ERROR: " << e.what() << "\n";
235  retval = 1;
236  }
237  }
238  }
239  return retval;
240  }
241  catch (const std::exception& e) {
242  std::cerr << "Caught exception: " << e.what() << "\n";
243  return 1;
244  }
245  catch (...) {
246  std::cerr << "Caught unknown exception\n";
247  return 1;
248  }
249 }