source: git/src/survexport.cc @ 04078a7

faster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 04078a7 was 0532954, checked in by Olly Betts <olly@…>, 16 months ago

Rework str.h

The previous implementation was O(n²) for a loop appending n characters
to the string. In practice the strings are typically very short for
.svx format, but for other formats they may be longer so it seems silly
to have this known inefficiency.

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