GeographicLib  2.6
Conformal3Proj.cpp
Go to the documentation of this file.
1 /**
2  * \file Conformal3Proj.cpp
3  * \brief Command line utility for computing geodesics on a triaxial ellipsoid
4  *
5  * Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  *
9  * See the <a href="Conformal3Proj.1.html">man page</a> for usage information.
10  **********************************************************************/
11 
12 // Usual flags
13 // -r reverse
14 // -e (supplement with -t)
15 // -w longfirst
16 // -p prec
17 // -h help
18 // -d dms
19 // -: colon
20 // --help
21 // --version
22 // --comment-delimiter
23 // + other I/O related flags
24 
25 // New flags
26 // -ex -tx
27 
28 #include <iostream>
29 #include <iomanip>
30 #include <string>
31 #include <sstream>
32 #include <fstream>
33 #include <GeographicLib/DMS.hpp>
35 #include <GeographicLib/Angle.hpp>
37 
38 #include "Conformal3Proj.usage"
39 
40 int main(int argc, const char* const argv[]) {
41  try {
42  using namespace GeographicLib;
43  using namespace Triaxial;
44  using real = Math::real;
45  using ang = Angle;
47  bool reverse = false, dms = false,
48  longfirst = false, altell = false;
49  real
53  e2 = -1, k2 = 0, kp2 = 0,
54  ax = -1, bx = 0, cx = 0, e2x = -1, k2x = 0, kp2x = 0;
55  int prec = 3;
56  std::string istring, ifile, ofile, cdelim;
57  char lsep = ';', dmssep = char(0);
58 
59  for (int m = 1; m < argc; ++m) {
60  std::string arg(argv[m]);
61  if (arg == "-r")
62  reverse = true;
63  else if (arg == "-t") {
64  if (m + 3 >= argc) return usage(1, true);
65  try {
66  a = Utility::val<real>(std::string(argv[m + 1]));
67  b = Utility::val<real>(std::string(argv[m + 2]));
68  c = Utility::val<real>(std::string(argv[m + 3]));
69  }
70  catch (const std::exception& e) {
71  std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
72  return 1;
73  }
74  e2 = -1;
75  m += 3;
76  } else if (arg == "-e") {
77  // Cayley ellipsoid sqrt([2,1,1/2]) is
78  // -e 1 3/2 1/3 2/3
79  if (m + 4 >= argc) return usage(1, true);
80  try {
81  b = Utility::val<real>(std::string(argv[m + 1]));
82  e2 = Utility::fract<real>(std::string(argv[m + 2]));
83  k2 = Utility::fract<real>(std::string(argv[m + 3]));
84  kp2 = Utility::fract<real>(std::string(argv[m + 4]));
85  }
86  catch (const std::exception& e) {
87  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
88  return 1;
89  }
90  a = -1;
91  m += 4;
92  } else if (arg == "-tx") {
93  if (m + 3 >= argc) return usage(1, true);
94  try {
95  ax = Utility::val<real>(std::string(argv[m + 1]));
96  bx = Utility::val<real>(std::string(argv[m + 2]));
97  cx = Utility::val<real>(std::string(argv[m + 3]));
98  }
99  catch (const std::exception& e) {
100  std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
101  return 1;
102  }
103  e2x = -1;
104  m += 3;
105  } else if (arg == "-ex") {
106  // Cayley ellipsoid sqrt([2,1,1/2]) is
107  // -e 1 3/2 1/3 2/3
108  if (m + 4 >= argc) return usage(1, true);
109  try {
110  bx = Utility::val<real>(std::string(argv[m + 1]));
111  e2x = Utility::fract<real>(std::string(argv[m + 2]));
112  k2x = Utility::fract<real>(std::string(argv[m + 3]));
113  kp2x = Utility::fract<real>(std::string(argv[m + 4]));
114  }
115  catch (const std::exception& e) {
116  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
117  return 1;
118  }
119  ax = -1;
120  m += 4;
121  } else if (arg == "-d") {
122  dms = true;
123  dmssep = '\0';
124  } else if (arg == "-:") {
125  dms = true;
126  dmssep = ':';
127  } else if (arg == "-w")
128  longfirst = !longfirst;
129  else if (arg == "-p") {
130  if (++m == argc) return usage(1, true);
131  try {
132  prec = Utility::val<int>(std::string(argv[m]));
133  }
134  catch (const std::exception&) {
135  std::cerr << "Precision " << argv[m] << " is not a number\n";
136  return 1;
137  }
138  } else if (arg == "--input-string") {
139  if (++m == argc) return usage(1, true);
140  istring = argv[m];
141  } else if (arg == "--input-file") {
142  if (++m == argc) return usage(1, true);
143  ifile = argv[m];
144  } else if (arg == "--output-file") {
145  if (++m == argc) return usage(1, true);
146  ofile = argv[m];
147  } else if (arg == "--line-separator") {
148  if (++m == argc) return usage(1, true);
149  if (std::string(argv[m]).size() != 1) {
150  std::cerr << "Line separator must be a single character\n";
151  return 1;
152  }
153  lsep = argv[m][0];
154  } else if (arg == "--comment-delimiter") {
155  if (++m == argc) return usage(1, true);
156  cdelim = argv[m];
157  } else if (arg == "--version") {
158  std::cout << argv[0] << ": GeographicLib version "
159  << GEOGRAPHICLIB_VERSION_STRING << "\n";
160  return 0;
161  } else
162  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
163  }
164 
165  Conformal3 tc(e2 >= 0 ? Ellipsoid3(b, e2, k2, kp2) : Ellipsoid3(a, b, c));
166  altell = e2x >= 0 || ax > 0;
167  Conformal3 tcx(e2x >= 0 ? Ellipsoid3(bx, e2x, k2x, kp2x) :
168  ax > 0 ? Ellipsoid3(ax, bx, cx) : Ellipsoid3());
169 
170  if (!ifile.empty() && !istring.empty()) {
171  std::cerr << "Cannot specify --input-string and --input-file together\n";
172  return 1;
173  }
174  if (ifile == "-") ifile.clear();
175  std::ifstream infile;
176  std::istringstream instring;
177  if (!ifile.empty()) {
178  infile.open(ifile.c_str());
179  if (!infile.is_open()) {
180  std::cerr << "Cannot open " << ifile << " for reading\n";
181  return 1;
182  }
183  } else if (!istring.empty()) {
184  std::string::size_type m = 0;
185  while (true) {
186  m = istring.find(lsep, m);
187  if (m == std::string::npos)
188  break;
189  istring[m] = '\n';
190  }
191  instring.str(istring);
192  }
193  std::istream* input = !ifile.empty() ? &infile :
194  (!istring.empty() ? &instring : &std::cin);
195 
196  std::ofstream outfile;
197  if (ofile == "-") ofile.clear();
198  if (!ofile.empty()) {
199  outfile.open(ofile.c_str());
200  if (!outfile.is_open()) {
201  std::cerr << "Cannot open " << ofile << " for writing\n";
202  return 1;
203  }
204  }
205  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
206 
207  // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
208  // 10^-11 sec (= 0.3 nm).
209  prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
210  using std::round, std::log10, std::ceil, std::signbit;
211  int disprec = std::max(0, prec + int(round(log10(6400000/b)))),
212  angprec = prec + 5, scalprec = prec + 7;
213  if (altell) {
214  scalprec += int(round(log10(b/bx)));
215  scalprec = std::max(0, scalprec);
216  }
217  std::string s, eol, stra, strb, strc;
218  std::istringstream str;
219  int retval = 0;
220  while (std::getline(*input, s)) {
221  try {
222  eol = "\n";
223  if (!cdelim.empty()) {
224  std::string::size_type m = s.find(cdelim);
225  if (m != std::string::npos) {
226  eol = " " + s.substr(m) + "\n";
227  s = s.substr(0, m);
228  }
229  }
230  // READ
231  str.clear(); str.str(s);
232  if (!(str >> stra >> strb))
233  throw GeographicErr("Incomplete input: " + s);
234  if (str >> strc)
235  throw GeographicErr("Extraneous input: " + strc);
236  ang gam{}; real k = 1;
237  if (reverse) {
238  ang bet, omg;
239  if (altell) {
240  ang betx, omgx;
241  ang::DecodeLatLon(stra, strb, betx, omgx, longfirst);
242  tc.ReverseOther(tcx, betx, omgx, bet, omg, gam, k);
243  } else {
244  real x = Utility::val<real>(stra), y = Utility::val<real>(strb);
245  tc.Reverse(x, y, bet, omg, k);
246  }
247  *output << ang::LatLonString(bet, omg,
248  angprec, dms, dmssep, longfirst);
249  } else {
250  ang bet, omg;
251  ang::DecodeLatLon(stra, strb, bet, omg, longfirst);
252  if (altell) {
253  ang betx, omgx;
254  tc.ForwardOther(tcx, bet, omg, betx, omgx, gam, k);
255  *output << ang::LatLonString(betx, omgx,
256  angprec, dms, dmssep, longfirst);
257  } else {
258  real x, y;
259  tc.Forward(bet, omg, x, y, k);
260  *output << Utility::str(x, disprec) << " "
261  << Utility::str(y, disprec);
262  }
263  }
264  if (altell)
265  *output << " " << ang::AzimuthString(gam, angprec, dms, dmssep);
266  *output << " " << Utility::str(k, scalprec) << eol;
267  }
268  catch (const std::exception& e) {
269  // Write error message cout so output lines match input lines
270  *output << "ERROR: " << e.what() << "\n";
271  retval = 1;
272  }
273  }
274  return retval;
275  }
276  catch (const std::exception& e) {
277  std::cerr << "Caught exception: " << e.what() << "\n";
278  return 1;
279  }
280  catch (...) {
281  std::cerr << "Caught unknown exception\n";
282  return 1;
283  }
284 }
static void DecodeLatLon(const std::string &stra, const std::string &strb, AngleT &lat, AngleT &lon, bool longfirst=false)
Definition: Angle.cpp:39
Header for GeographicLib::Utility class.
int main(int argc, const char *const argv[])
AngleT< Math::real > Angle
Definition: Angle.hpp:760
static int extra_digits()
Definition: Math.cpp:49
int usage(int retval, bool)
Definition: Geod3ODE.cpp:41
Header for GeographicLib::Triaxial::Conformal3 class.
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
static std::string AzimuthString(AngleT azi, int prec, bool dms=false, char dmssep='\0')
Definition: Angle.cpp:86
static std::string str(T x, int p=-1)
Definition: Utility.hpp:161
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
static int set_digits(int ndigits=0)
Definition: Utility.cpp:184
Exception handling for GeographicLib.
Definition: Constants.hpp:344
static std::string LatLonString(AngleT lat, AngleT lon, int prec, bool dms=false, char dmssep='\0', bool longfirst=false)
Definition: Angle.cpp:72
Header for the GeographicLib::AngleT class.
Header for GeographicLib::DMS class.