source: git/src/survexport.cc @ 3859ed3

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-datawalls-data-hanging-as-warning
Last change on this file since 3859ed3 was df121c2d, checked in by Olly Betts <olly@…>, 6 years ago

Support exporting as CSV

This is just the Survex POS output filter with a minor formatting change.

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