source: git/src/survexport.cc @ 8553bdb

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernlogwalls-datawalls-data-hanging-as-warning
Last change on this file since 8553bdb was 32a040e, checked in by Olly Betts <olly@…>, 6 years ago

Support "clamp to ground" for KML export

The default altitude mode for KML is "clampToGround", which renders
data on the surface of the terrain. This is useful with KML viewers
which render the terrain as opaque so underground data isn't visible.
Rendering cave passages on the surface isn't great, but is better
than not being able to see them at all.

This option may also be helpful if you want to see where to look on the
surface for new entrances.

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