source: git/src/survexport.cc @ 350a042

stereo-2025
Last change on this file since 350a042 was 1854ba1, checked in by Olly Betts <olly@…>, 11 months ago

Remove Skencil export support

The last release of Skencil was in 2005. There was an attempt to
revive the project in 2010, but that didn't lead to another release
and seems to have petered out. No current Linux distro (or other
package system) seems to have packages for it. The current git
version still appears to require Python 2.

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