GeographicLib 2.7

Loading...
Searching...
No Matches
Cart3Convert.cpp
Go to the documentation of this file.
1/**
2 * \file Cart3Convert.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="Cart3Convert.1.html">man page</a> for usage information.
10 **********************************************************************/
11
12// Counterpart of CartConvert
13
14// Usual flags
15// -r (x, y, z to ellipsoidal to geographic)
16// -e (supplement with -t)
17// -w longfirst
18// -p prec
19// -h help
20// -d dms
21// -: colon
22// --help
23// --version
24// --comment-delimiter
25// + other I/O related flags
26
27// New flags
28// -E ellipsoidal coords (default)
29// -G geodetic
30// -P parametric
31// -C geocentric
32// -3 include height (only for -E and -G)
33// -D include direction (only for -E without -3)
34
35// Can't specify -3 and -D together
36// Height for -G has its usual definition
37// Height for -E is the increase in the minor semiaxis for the confocal
38// ellipsoid (H = u - c).
39
40#include <iostream>
41#include <iomanip>
42#include <string>
43#include <sstream>
44#include <fstream>
45#include <GeographicLib/DMS.hpp>
46#include <GeographicLib/Utility.hpp>
47#include <GeographicLib/Angle.hpp>
48#include <GeographicLib/Triaxial/Cartesian3.hpp>
49
50#include "Cart3Convert.usage"
51
52 int main(int argc, const char* const argv[]) {
53 try {
54 using namespace GeographicLib;
55 using namespace Triaxial;
56 Utility::set_digits();
57 using real = Math::real;
58 using ang = Angle;
59 using vec3 = Ellipsoid3::vec3;
60 using coord = Cartesian3::coord;
61 enum { GEODETIC, PARAMETRIC, GEOCENTRIC, ELLIPSOIDAL };
62 coord mode = coord::ELLIPSOIDAL;
63 bool threed = false, direction = false, reverse = false, dms = false,
64 longfirst = false, randompts = false;
65 real
66 a = Constants::Triaxial_Earth_a(),
67 b = Constants::Triaxial_Earth_b(),
68 c = Constants::Triaxial_Earth_c(),
69 e2 = -1, k2 = 0, kp2 = 0;
70 int prec = 3, nrand = 0;
71 unsigned long long seed = 0;
72 std::string istring, ifile, ofile, cdelim;
73 char lsep = ';', dmssep = char(0);
74
75 for (int m = 1; m < argc; ++m) {
76 std::string arg(argv[m]);
77 if (arg == "-E")
78 mode = coord::ELLIPSOIDAL;
79 else if (arg == "-G")
80 mode = coord::GEODETIC;
81 else if (arg == "-P")
82 mode = coord::PARAMETRIC;
83 else if (arg == "-C")
84 mode = coord::GEOCENTRIC;
85 else if (arg == "-GX")
86 mode = coord::GEODETIC_X;
87 else if (arg == "-PX")
88 mode = coord::PARAMETRIC_X;
89 else if (arg == "-CX")
90 mode = coord::GEOCENTRIC_X;
91 else if (arg == "-R") {
92 if (++m == argc) return usage(1, true);
93 try {
94 nrand = Utility::val<int>(std::string(argv[m]));
95 }
96 catch (const std::exception&) {
97 std::cerr << "Number of randoms " << argv[m] << " is not a number\n";
98 return 1;
99 }
100 randompts = true;
101 } else if (arg == "-3")
102 threed = true;
103 else if (arg == "-D")
104 direction = true;
105 else if (arg == "-r")
106 reverse = true;
107 else if (arg == "--seed") {
108 if (++m == argc) return usage(1, true);
109 try {
110 seed = Utility::val<unsigned long long>(std::string(argv[m]));
111 }
112 catch (const std::exception&) {
113 std::cerr << "Precision " << argv[m] << " is not a number\n";
114 return 1;
115 }
116 } else if (arg == "-t") {
117 if (m + 3 >= argc) return usage(1, true);
118 try {
119 a = Utility::val<real>(std::string(argv[m + 1]));
120 b = Utility::val<real>(std::string(argv[m + 2]));
121 c = Utility::val<real>(std::string(argv[m + 3]));
122 }
123 catch (const std::exception& e) {
124 std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
125 return 1;
126 }
127 e2 = -1;
128 m += 3;
129 } else if (arg == "-e") {
130 // Cayley ellipsoid sqrt([2,1,1/2]) is
131 // -e 1 3/2 1/3 2/3
132 if (m + 4 >= argc) return usage(1, true);
133 try {
134 b = Utility::val<real>(std::string(argv[m + 1]));
135 e2 = Utility::fract<real>(std::string(argv[m + 2]));
136 k2 = Utility::fract<real>(std::string(argv[m + 3]));
137 kp2 = Utility::fract<real>(std::string(argv[m + 4]));
138 }
139 catch (const std::exception& e) {
140 std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
141 return 1;
142 }
143 a = -1;
144 m += 4;
145 } else if (arg == "-d") {
146 dms = true;
147 dmssep = '\0';
148 } else if (arg == "-:") {
149 dms = true;
150 dmssep = ':';
151 } else if (arg == "-w")
152 longfirst = !longfirst;
153 else if (arg == "-p") {
154 if (++m == argc) return usage(1, true);
155 try {
156 prec = Utility::val<int>(std::string(argv[m]));
157 }
158 catch (const std::exception&) {
159 std::cerr << "Precision " << argv[m] << " is not a number\n";
160 return 1;
161 }
162 } else if (arg == "--input-string") {
163 if (++m == argc) return usage(1, true);
164 istring = argv[m];
165 } else if (arg == "--input-file") {
166 if (++m == argc) return usage(1, true);
167 ifile = argv[m];
168 } else if (arg == "--output-file") {
169 if (++m == argc) return usage(1, true);
170 ofile = argv[m];
171 } else if (arg == "--line-separator") {
172 if (++m == argc) return usage(1, true);
173 if (std::string(argv[m]).size() != 1) {
174 std::cerr << "Line separator must be a single character\n";
175 return 1;
176 }
177 lsep = argv[m][0];
178 } else if (arg == "--comment-delimiter") {
179 if (++m == argc) return usage(1, true);
180 cdelim = argv[m];
181 } else if (arg == "--version") {
182 std::cout << argv[0] << ": GeographicLib version "
183 << GEOGRAPHICLIB_VERSION_STRING << "\n";
184 return 0;
185 } else
186 return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
187 }
188
189 Cartesian3 tc(e2 >= 0 ? Ellipsoid3(b, e2, k2, kp2) : Ellipsoid3(a, b, c));
190
191 if (randompts) {
192 if (threed) {
193 std::cerr << "Cannot specify -3 for random points\n";
194 return 1;
195 }
196 if (!ifile.empty() || !istring.empty()) {
197 std::cerr
198 << "Cannot specify --input-{string,file} with random points\n";
199 }
200 } else {
201 if (direction && threed) {
202 std::cerr << "Cannot specify both -3 and -D\n";
203 return 1;
204 }
205 }
206
207 if (!ifile.empty() && !istring.empty()) {
208 std::cerr << "Cannot specify --input-string and --input-file together\n";
209 return 1;
210 }
211 if (ifile == "-") ifile.clear();
212 std::ifstream infile;
213 std::istringstream instring;
214 if (!ifile.empty()) {
215 infile.open(ifile.c_str());
216 if (!infile.is_open()) {
217 std::cerr << "Cannot open " << ifile << " for reading\n";
218 return 1;
219 }
220 } else if (!istring.empty()) {
221 std::string::size_type m = 0;
222 while (true) {
223 m = istring.find(lsep, m);
224 if (m == std::string::npos)
225 break;
226 istring[m] = '\n';
227 }
228 instring.str(istring);
229 }
230 std::istream* input = !ifile.empty() ? &infile :
231 (!istring.empty() ? &instring : &std::cin);
232
233 std::ofstream outfile;
234 if (ofile == "-") ofile.clear();
235 if (!ofile.empty()) {
236 outfile.open(ofile.c_str());
237 if (!outfile.is_open()) {
238 std::cerr << "Cannot open " << ofile << " for writing\n";
239 return 1;
240 }
241 }
242 std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
243
244 // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
245 // 10^-11 sec (= 0.3 nm).
246 prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
247 using std::round, std::log10, std::ceil, std::signbit;
248 int disprec = std::max(0, prec + int(round(log10(6400000/b)))),
249 angprec = prec + 5, vecprec = prec + 7;
250 if (randompts) {
251 // C a r t 3 C o n
252 unsigned long long s1 = 0x4361727433436f6eULL, s2 = seed;
253 if (seed == 0) {
254 s1 = std::random_device()();
255 s2 = std::random_device()();
256 }
257 std::seed_seq seq{s1, s2};
258 std::mt19937 g(seq);
259 for (int i = 0; i < nrand; ++i) {
260 vec3 r, v = {0,0,0};
261 if (direction)
262 tc.cart2rand(g, r, v);
263 else
264 tc.cart2rand(g, r);
265 if (!reverse) {
266 *output << Utility::str(r[0], disprec) << " "
267 << Utility::str(r[1], disprec) << " "
268 << Utility::str(r[2], disprec);
269 if (direction)
270 *output << " "
271 << Utility::str(v[0], vecprec) << " "
272 << Utility::str(v[1], vecprec) << " "
273 << Utility::str(v[2], vecprec);
274 } else {
275 ang bet, omg, alp;
276 tc.cart2toany(r, v, mode, bet, omg, alp);
277 *output << ang::LatLonString(bet, omg,
278 angprec, dms, dmssep, longfirst);
279 if (direction)
280 *output << " " << ang::AzimuthString(alp, angprec, dms, dmssep);
281 }
282 *output << "\n";
283 }
284 return 0;
285 }
286 std::string s, eol, sbet, somg, salp, sh, sx, sy, sz, svx, svy, svz, strc;
287 std::istringstream str;
288 int retval = 0;
289 vec3 r = {0,0,0}, v = {0,0,0};
290 ang bet, omg, alp;
291 real h = 0;
292 while (std::getline(*input, s)) {
293 try {
294 eol = "\n";
295 if (!cdelim.empty()) {
296 std::string::size_type m = s.find(cdelim);
297 if (m != std::string::npos) {
298 eol = " " + s.substr(m) + "\n";
299 s = s.substr(0, m);
300 }
301 }
302 // READ
303 str.clear(); str.str(s);
304 if (reverse) {
305 if (!(str >> sx >> sy >> sz))
306 throw GeographicErr("Incomplete input: " + s);
307 r[0] = Utility::val<real>(sx);
308 r[1] = Utility::val<real>(sy);
309 r[2] = Utility::val<real>(sz);
310 if (direction) {
311 if (!(str >> svx >> svy >> svz))
312 throw GeographicErr("Incomplete input: " + s);
313 v[0] = Utility::val<real>(svx);
314 v[1] = Utility::val<real>(svy);
315 v[2] = Utility::val<real>(svz);
316 }
317 } else {
318 if (!(str >> sbet >> somg))
319 throw GeographicErr("Incomplete input: " + s);
320 ang::DecodeLatLon(sbet, somg, bet, omg, longfirst);
321 if (mode != coord::ELLIPSOIDAL && (bet.n() != 0 || signbit(bet.c())))
322 throw GeographicErr("Latitude outside range [-90,90]: " + s);
323 if (threed) {
324 if (!(str >> sh))
325 throw GeographicErr("Incomplete input: " + s);
326 h = Utility::val<real>(sh);
327 } else if (direction) {
328 if (!(str >> salp))
329 throw GeographicErr("Incomplete input: " + s);
330 alp = ang::DecodeAzimuth(salp);
331 }
332 }
333 if (str >> strc)
334 throw GeographicErr("Extraneous input: " + strc);
335 // PROCESS
336 if (reverse) {
337 if (threed)
338 tc.carttoany(r, mode, bet, omg, h);
339 else if (direction)
340 tc.cart2toany(r, v, mode, bet, omg, alp);
341 else
342 tc.cart2toany(r, mode, bet, omg);
343 } else {
344 if (threed)
345 tc.anytocart(mode, bet, omg, h, r);
346 else if (direction)
347 tc.anytocart2(mode, bet, omg, alp, r, v);
348 else
349 tc.anytocart2(mode, bet, omg, r);
350 }
351 // WRITE
352 if (reverse) {
353 *output << ang::LatLonString(bet, omg,
354 angprec, dms, dmssep, longfirst);
355 if (threed)
356 *output << " " << Utility::str(h, disprec);
357 else if (direction)
358 *output << " " << ang::AzimuthString(alp, angprec, dms, dmssep);
359 } else {
360 *output << Utility::str(r[0], disprec) << " "
361 << Utility::str(r[1], disprec) << " "
362 << Utility::str(r[2], disprec);
363 if (direction)
364 *output << " "
365 << Utility::str(v[0], vecprec) << " "
366 << Utility::str(v[1], vecprec) << " "
367 << Utility::str(v[2], vecprec);
368 }
369 *output << eol;
370 }
371 catch (const std::exception& e) {
372 // Write error message cout so output lines match input lines
373 *output << "ERROR: " << e.what() << "\n";
374 retval = 1;
375 }
376 }
377 return retval;
378 }
379 catch (const std::exception& e) {
380 std::cerr << "Caught exception: " << e.what() << "\n";
381 return 1;
382 }
383 catch (...) {
384 std::cerr << "Caught unknown exception\n";
385 return 1;
386 }
387}
Angle.hpp
Header for the GeographicLib::AngleT class.
main
int main(int argc, const char *const argv[])
Definition Cart3Convert.cpp:52
Cartesian3.hpp
Header for GeographicLib::Triaxial::Cartesian3 class.
DMS.hpp
Header for GeographicLib::DMS class.
usage
int usage(int retval, bool)
Definition Geod3ODE.cpp:41
ang
GeographicLib::Angle ang
Definition Geod3Solve.cpp:26
real
GeographicLib::Math::real real
Definition Geod3Solve.cpp:25
Utility.hpp
Header for GeographicLib::Utility class.
GeographicLib::AngleT::c
T c() const
Definition Angle.hpp:153
GeographicLib::AngleT< Math::real >::DecodeLatLon
static void DecodeLatLon(const std::string &stra, const std::string &strb, AngleT &lat, AngleT &lon, bool longfirst=false)
GeographicLib::AngleT< Math::real >::LatLonString
static std::string LatLonString(AngleT lat, AngleT lon, int prec, bool dms=false, char dmssep='\0', bool longfirst=false)
GeographicLib::AngleT< Math::real >::DecodeAzimuth
static AngleT DecodeAzimuth(const std::string &azistr)
GeographicLib::AngleT::n
T n() const
Definition Angle.hpp:161
GeographicLib::AngleT< Math::real >::AzimuthString
static std::string AzimuthString(AngleT azi, int prec, bool dms=false, char dmssep='\0')
GeographicLib::Constants::Triaxial_Earth_b
static T Triaxial_Earth_b()
Definition Constants.hpp:218
GeographicLib::Constants::Triaxial_Earth_a
static T Triaxial_Earth_a()
Definition Constants.hpp:211
GeographicLib::Constants::Triaxial_Earth_c
static T Triaxial_Earth_c()
Definition Constants.hpp:225
GeographicLib::GeographicErr
Exception handling for GeographicLib.
Definition Constants.hpp:344
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib::Math::extra_digits
static int extra_digits()
Definition Math.cpp:49
GeographicLib::Utility::fract
static T fract(const std::string &s)
Definition Utility.hpp:298
GeographicLib::Utility::val
static T val(const std::string &s)
Definition Utility.hpp:225
GeographicLib::Utility::set_digits
static int set_digits(int ndigits=0)
Definition Utility.cpp:184
GeographicLib::Utility::str
static std::string str(T x, int p=-1)
Definition Utility.hpp:161
GeographicLib::Triaxial
Namespace for operations on triaxial ellipsoids.
Definition Cartesian3.cpp:13
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
GeographicLib::Angle
AngleT< Math::real > Angle
Definition Angle.hpp:760