source: git/src/survexport.cc @ f96897b

faster-cavernlogwalls-datawalls-data-hanging-as-warning
Last change on this file since f96897b was 4c83f84, checked in by Olly Betts <olly@…>, 4 weeks ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

  • Property mode set to 100644
File size: 14.1 KB
Line 
1/* survexport.cc
2 * Convert a processed survey data file to another format.
3 */
4
5/* Copyright (C) 1994-2024 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 */
22
23#include <config.h>
24
25#define MSG_SETUP_PROJ_SEARCH_PATH 1
26
27#include <ctype.h>
28#include <math.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "export.h"
34#include "mainfrm.h"
35
36#include "cmdline.h"
37#include "filename.h"
38#include "img_hosted.h"
39#include "message.h"
40#include "useful.h"
41
42#include <iostream>
43#include <string>
44
45using namespace std;
46
47int
48main(int argc, char **argv)
49{
50   double pan = 0;
51   double tilt = -90.0;
52   export_format format = FMT_MAX_PLUS_ONE_;
53   int show_mask = 0;
54   const char *survey = NULL;
55   double grid = 0.0; /* grid spacing (or 0 for no grid) */
56   double text_height = DEFAULT_TEXT_HEIGHT; /* for station labels */
57   double marker_size = DEFAULT_MARKER_SIZE; /* for station markers */
58   double scale = 500.0;
59   SurveyFilter* filter = NULL;
60
61   {
62       /* Default to .pos output if installed as 3dtopos. */
63       char* progname = baseleaf_from_fnm(argv[0]);
64       for (char * p = progname; *p; ++p) {
65           *p = tolower((unsigned char)*p);
66       }
67       if (strcmp(progname, "3dtopos") == 0) {
68           format = FMT_POS;
69       }
70       osfree(progname);
71   }
72
73   const int OPT_FMT_BASE = 20000;
74   enum {
75       OPT_SCALE = 0x100, OPT_BEARING, OPT_TILT, OPT_PLAN, OPT_ELEV,
76       OPT_LEGS, OPT_SURF, OPT_SPLAYS, OPT_CROSSES, OPT_LABELS, OPT_ENTS,
77       OPT_FIXES, OPT_EXPORTS, OPT_XSECT, OPT_WALLS, OPT_PASG,
78       OPT_CENTRED, OPT_FULL_COORDS, OPT_CLAMP_TO_GROUND, OPT_DEFAULTS
79   };
80   static const struct option long_opts[] = {
81        /* const char *name; int has_arg (0 no_argument, 1 required, 2 options_*); int *flag; int val */
82        {"survey", required_argument, 0, 's'},
83        {"scale", required_argument, 0, OPT_SCALE},
84        {"bearing", required_argument, 0, OPT_BEARING},
85        {"tilt", required_argument, 0, OPT_TILT},
86        {"plan", no_argument, 0, OPT_PLAN},
87        {"elevation", no_argument, 0, OPT_ELEV},
88        {"legs", no_argument, 0, OPT_LEGS},
89        {"surface-legs", no_argument, 0, OPT_SURF},
90        {"splays", no_argument, 0, OPT_SPLAYS},
91        {"crosses", no_argument, 0, OPT_CROSSES},
92        {"station-names", no_argument, 0, OPT_LABELS},
93        {"entrances", no_argument, 0, OPT_ENTS},
94        {"fixes", no_argument, 0, OPT_FIXES},
95        {"exports", no_argument, 0, OPT_EXPORTS},
96        {"cross-sections", no_argument, 0, OPT_XSECT},
97        {"walls", no_argument, 0, OPT_WALLS},
98        {"passages", no_argument, 0, OPT_PASG},
99        {"origin-in-centre", no_argument, 0, OPT_CENTRED},
100        {"full-coordinates", no_argument, 0, OPT_FULL_COORDS},
101        {"clamp-to-ground", no_argument, 0, OPT_CLAMP_TO_GROUND},
102        {"defaults", no_argument, 0, OPT_DEFAULTS},
103        {"grid", optional_argument, 0, 'g'},
104        {"text-height", required_argument, 0, 't'},
105        {"marker-size", required_argument, 0, 'm'},
106        {"3d", no_argument, 0, OPT_FMT_BASE + FMT_3D},
107        {"csv", no_argument, 0, OPT_FMT_BASE + FMT_CSV},
108        {"dxf", no_argument, 0, OPT_FMT_BASE + FMT_DXF},
109        {"eps", no_argument, 0, OPT_FMT_BASE + FMT_EPS},
110        {"gpx", no_argument, 0, OPT_FMT_BASE + FMT_GPX},
111        {"hpgl", no_argument, 0, OPT_FMT_BASE + FMT_HPGL},
112        {"json", no_argument, 0, OPT_FMT_BASE + FMT_JSON},
113        {"kml", no_argument, 0, OPT_FMT_BASE + FMT_KML},
114        {"plt", no_argument, 0, OPT_FMT_BASE + FMT_PLT},
115        {"skencil", no_argument, 0, OPT_FMT_BASE + FMT_SK},
116        {"pos", no_argument, 0, OPT_FMT_BASE + FMT_POS},
117        {"svg", no_argument, 0, OPT_FMT_BASE + FMT_SVG},
118        {"help", no_argument, 0, HLP_HELP},
119        {"version", no_argument, 0, HLP_VERSION},
120        // US spelling:
121        {"origin-in-center", no_argument, 0, OPT_CENTRED},
122        // Abbreviation:
123        {"full-coords", no_argument, 0, OPT_FULL_COORDS},
124        {0,0,0,0}
125   };
126
127#define short_opts "s:g::t:m:"
128
129   static struct help_msg help[] = {
130        /*                      <-- */
131        {HLP_ENCODELONG(0),   /*only load the sub-survey with this prefix*/199, 0},
132        /* TRANSLATORS: These example input values should not be translated. */
133        {HLP_ENCODELONG(1),   /*scale (50, 0.02, 1:50 and 2:100 all mean 1:50)*/217, 0},
134        /* TRANSLATORS: These example input values should not be translated. */
135        {HLP_ENCODELONG(2),   /*bearing (90, 90d, 100g all mean 90°)*/460, 0},
136        /* TRANSLATORS: These example input values should not be translated. */
137        {HLP_ENCODELONG(3),   /*tilt (45, 45d, 50g, 100% all mean 45°)*/461, 0},
138        /* TRANSLATORS: Don't translate example command line option --tilt=-90 */
139        {HLP_ENCODELONG(4),   /*plan view (equivalent to --tilt=-90)*/462, 0},
140        /* TRANSLATORS: Don't translate example command line option --tilt=0 */
141        {HLP_ENCODELONG(5),   /*elevation view (equivalent to --tilt=0)*/463, 0},
142        {HLP_ENCODELONG(6),   /*underground survey legs*/476, 0},
143        {HLP_ENCODELONG(7),   /*surface survey legs*/464, 0},
144        {HLP_ENCODELONG(8),   /*splay legs*/465, 0},
145        {HLP_ENCODELONG(9),   /*station markers*/474, 0},
146        {HLP_ENCODELONG(10),  /*station labels*/475, 0},
147        {HLP_ENCODELONG(11),  /*entrances*/466, 0},
148        {HLP_ENCODELONG(12),  /*fixed points*/467, 0},
149        {HLP_ENCODELONG(13),  /*exported stations*/468, 0},
150        {HLP_ENCODELONG(14),  /*cross-sections*/469, 0},
151        {HLP_ENCODELONG(15),  /*walls*/470, 0},
152        {HLP_ENCODELONG(16),  /*passages*/471, 0},
153        {HLP_ENCODELONG(17),  /*origin in centre*/472, 0},
154        {HLP_ENCODELONG(18),  /*full coordinates*/473, 0},
155        {HLP_ENCODELONG(19),  /*clamp to ground*/478, 0},
156        {HLP_ENCODELONG(20),  /*include items exported by default*/155, 0},
157        {HLP_ENCODELONG(21),  /*generate grid (default %sm)*/148, STRING(DEFAULT_GRID_SPACING)},
158        {HLP_ENCODELONG(22),  /*station labels text height (default %s)*/149, STRING(DEFAULT_TEXT_HEIGHT)},
159        {HLP_ENCODELONG(23),  /*station marker size (default %s)*/152, STRING(DEFAULT_MARKER_SIZE)},
160        {HLP_ENCODELONG(24),  /*produce Survex 3d output*/487, 0},
161        {HLP_ENCODELONG(25),  /*produce CSV output*/102, 0},
162        {HLP_ENCODELONG(26),  /*produce DXF output*/156, 0},
163        {HLP_ENCODELONG(27),  /*produce EPS output*/454, 0},
164        {HLP_ENCODELONG(28),  /*produce GPX output*/455, 0},
165        {HLP_ENCODELONG(29),  /*produce HPGL output*/456, 0},
166        {HLP_ENCODELONG(30),  /*produce JSON output*/457, 0},
167        {HLP_ENCODELONG(31),  /*produce KML output*/458, 0},
168        /* TRANSLATORS: "Compass" and "Carto" are the names of software packages,
169         * so should not be translated. */
170        {HLP_ENCODELONG(32),  /*produce Compass PLT output for Carto*/159, 0},
171        /* TRANSLATORS: "Skencil" is the name of a software package, so should not be
172         * translated. */
173        {HLP_ENCODELONG(33),  /*produce Skencil output*/158, 0},
174        {HLP_ENCODELONG(34),  /*produce Survex POS output*/459, 0},
175        {HLP_ENCODELONG(35),  /*produce SVG output*/160, 0},
176        {0, 0, 0}
177   };
178
179   msg_init(argv);
180
181   string optmap[sizeof(show_mask) * CHAR_BIT];
182
183   int long_index;
184   bool always_include_defaults = false;
185   cmdline_init(argc, argv, short_opts, long_opts, &long_index, help, 1, 2);
186   while (1) {
187      long_index = -1;
188      int opt = cmdline_getopt();
189      if (opt == EOF) break;
190      int bit = 0;
191      switch (opt) {
192       case OPT_LEGS:
193         bit = LEGS;
194         break;
195       case OPT_SURF:
196         bit = SURF;
197         break;
198       case OPT_SPLAYS:
199         bit = SPLAYS;
200         break;
201       case OPT_CROSSES:
202         bit = STNS;
203         break;
204       case OPT_LABELS:
205         bit = LABELS;
206         break;
207       case OPT_ENTS:
208         bit = ENTS;
209         break;
210       case OPT_FIXES:
211         bit = FIXES;
212         break;
213       case OPT_EXPORTS:
214         bit = EXPORTS;
215         break;
216       case OPT_XSECT:
217         bit = XSECT;
218         break;
219       case OPT_WALLS:
220         bit = WALLS;
221         break;
222       case OPT_PASG:
223         bit = PASG;
224         break;
225       case OPT_CENTRED:
226         bit = CENTRED;
227         break;
228       case OPT_FULL_COORDS:
229         bit = FULL_COORDS;
230         break;
231       case OPT_CLAMP_TO_GROUND:
232         bit = CLAMP_TO_GROUND;
233         break;
234       case OPT_DEFAULTS:
235         always_include_defaults = true;
236         break;
237       case 'g': /* Grid */
238         if (optarg) {
239            grid = cmdline_double_arg();
240         } else {
241            grid = (double)DEFAULT_GRID_SPACING;
242         }
243         bit = GRID;
244         break;
245       case OPT_SCALE: {
246         char* colon = strchr(optarg, ':');
247         if (!colon) {
248             /* --scale=1000 => 1:1000 => scale = 1000 */
249             scale = cmdline_double_arg();
250             if (scale < 1.0) {
251                 /* --scale=0.001 => 1:1000 => scale = 1000 */
252                 scale = 1.0 / scale;
253             }
254         } else if (colon - optarg == 1 && optarg[0] == '1') {
255             /* --scale=1:1000 => 1:1000 => scale = 1000 */
256             optarg += 2;
257             scale = cmdline_double_arg();
258             optarg -= 2;
259         } else {
260             /* --scale=2:1000 => 1:500 => scale = 500 */
261             *colon = '\0';
262             scale = cmdline_double_arg();
263             optarg = colon + 1;
264             scale = cmdline_double_arg() / scale;
265             *colon = ':';
266         }
267         bit = SCALE;
268         break;
269       }
270       case OPT_BEARING: {
271         int units = 0;
272         size_t len = strlen(optarg);
273         if (len > 0) {
274             char ch = optarg[len - 1];
275             switch (ch) {
276                 case 'd':
277                 case 'g':
278                     units = ch;
279                     optarg[len - 1] = '\0';
280                     break;
281             }
282             pan = cmdline_double_arg();
283             optarg[len - 1] = ch;
284         } else {
285             pan = cmdline_double_arg();
286         }
287         if (units == 'g') {
288             pan *= 0.9;
289         }
290         bit = ORIENTABLE;
291         break;
292       }
293       case OPT_TILT: {
294         int units = 0;
295         size_t len = strlen(optarg);
296         if (len > 0) {
297             char ch = optarg[len - 1];
298             switch (ch) {
299                 case '%':
300                 case 'd':
301                 case 'g':
302                     units = ch;
303                     optarg[len - 1] = '\0';
304                     break;
305             }
306             tilt = cmdline_double_arg();
307             optarg[len - 1] = ch;
308         } else {
309             tilt = cmdline_double_arg();
310         }
311         if (units == 'g') {
312             tilt *= 0.9;
313         } else if (units == '%') {
314             tilt = deg(atan(tilt * 0.01));
315         }
316         bit = ORIENTABLE;
317         break;
318       }
319       case OPT_PLAN:
320         tilt = -90.0;
321         bit = ORIENTABLE;
322         break;
323       case OPT_ELEV:
324         tilt = 0.0;
325         bit = ORIENTABLE;
326         break;
327       case 't': /* Text height */
328         text_height = cmdline_double_arg();
329         bit = TEXT_HEIGHT;
330         break;
331       case 'm': /* Marker size */
332         marker_size = cmdline_double_arg();
333         bit = MARKER_SIZE;
334         break;
335       case 's':
336         if (survey) {
337             if (!filter) {
338                 filter = new SurveyFilter();
339                 filter->add(survey);
340             }
341             filter->add(optarg);
342         } else {
343             survey = optarg;
344         }
345         break;
346       default:
347         if (opt >= OPT_FMT_BASE && opt < OPT_FMT_BASE + FMT_MAX_PLUS_ONE_) {
348             format = export_format(opt - OPT_FMT_BASE);
349         }
350      }
351      if (bit) {
352          show_mask |= bit;
353          int i = 0;
354          while (((bit >> i) & 1) == 0) ++i;
355
356          if (!optmap[i].empty()) optmap[i] += ' ';
357
358          // Reconstruct what the command line option was.
359          if (long_index < 0) {
360              optmap[i] += '-';
361              optmap[i] += char(opt);
362              if (optarg) {
363                  if (optarg == argv[optind - 1]) {
364                      optmap[i] += ' ';
365                  }
366                  optmap[i] += optarg;
367              }
368          } else {
369              optmap[i] += "--";
370              optmap[i] += long_opts[long_index].name;
371              if (optarg) {
372                  if (optarg == argv[optind - 1]) {
373                      optmap[i] += ' ';
374                  } else {
375                      optmap[i] += '=';
376                  }
377                  optmap[i] += optarg;
378              }
379          }
380      }
381   }
382
383   // A single --survey is handled by img at load-time.  Multiple --survey are
384   // handled via a SurveyFilter at export time.
385   if (filter) survey = NULL;
386
387   const char* fnm_in = argv[optind++];
388   const char* fnm_out = argv[optind];
389   if (fnm_out) {
390      if (format == FMT_MAX_PLUS_ONE_) {
391         // Select format based on extension.
392         size_t len = strlen(fnm_out);
393         // Length of longest extension of interest.
394         constexpr size_t MAX_EXT_LEN = 4;
395         char ext[MAX_EXT_LEN + 2];
396         for (size_t i = 0; i < MAX_EXT_LEN + 2; ++i) {
397             ext[i] = tolower((unsigned char)fnm_out[len - (MAX_EXT_LEN + 1) + i]);
398         }
399         for (size_t i = 0; i < FMT_MAX_PLUS_ONE_; ++i) {
400            const auto& info = export_format_info[i];
401            size_t l = strlen(info.extension);
402            if (len > l + 1 &&
403                strcmp(ext + MAX_EXT_LEN + 1 - l, info.extension) == 0) {
404               format = export_format(i);
405               break;
406            }
407         }
408         if (format == FMT_MAX_PLUS_ONE_) {
409            fatalerror(/*Export format not specified and not known from output file extension*/252);
410         }
411      }
412   } else {
413      if (format == FMT_MAX_PLUS_ONE_) {
414         fatalerror(/*Export format not specified*/253);
415      }
416      char *baseleaf = baseleaf_from_fnm(fnm_in);
417      /* note : memory allocated by fnm_out gets leaked in this case... */
418      fnm_out = add_ext(baseleaf, export_format_info[format].extension);
419      osfree(baseleaf);
420   }
421
422   const auto& format_info_mask = export_format_info[format].mask;
423   unsigned not_allowed = show_mask &~ format_info_mask;
424   if (not_allowed) {
425       printf("warning: The following options are not supported for this export format and will be ignored:\n");
426       int i = 0;
427       unsigned bit = 1;
428       while (not_allowed) {
429           if (not_allowed & bit) {
430               // E.g. --walls maps to two bits in show_mask, but the options
431               // are only put on the least significant in such cases.
432               if (!optmap[i].empty())
433                   printf("%s\n", optmap[i].c_str());
434               not_allowed &= ~bit;
435           }
436           ++i;
437           bit <<= 1;
438       }
439       show_mask &= format_info_mask;
440   }
441
442   if (always_include_defaults || show_mask == 0) {
443       show_mask |= export_format_info[format].defaults;
444   }
445
446   if (!(format_info_mask & ORIENTABLE)) {
447       pan = 0.0;
448       tilt = -90.0;
449   }
450
451   Model model;
452   int err = model.Load(fnm_in, survey);
453   if (err) fatalerror(err, fnm_in);
454   if (filter) filter->SetSeparator(model.GetSeparator());
455
456   try {
457       if (!Export(fnm_out, model.GetSurveyTitle(),
458                   model.GetDateString(),
459                   model, filter,
460                   pan, tilt, show_mask, format,
461                   grid, text_height, marker_size,
462                   scale)) {
463          fatalerror(/*Couldn’t write file “%s”*/402, fnm_out);
464       }
465   } catch (const wxString & m) {
466       wxString r = msg_appname();
467       r += ": ";
468       r += wmsg(/*error*/93);
469       r += ": ";
470       r += m;
471       wcerr << r.c_str() << '\n';
472   }
473
474   return 0;
475}
Note: See TracBrowser for help on using the repository browser.