2 * Convert a processed survey data file to another format.
5 /* Copyright (C) 1994-2024 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
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.
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.
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
25 #define MSG_SETUP_PROJ_SEARCH_PATH 1
38 #include "img_hosted.h"
49 main(int argc
, char **argv
)
53 export_format format
= FMT_MAX_PLUS_ONE_
;
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 */
60 SurveyFilter
* filter
= NULL
;
63 /* Default to .pos output if installed as 3dtopos. */
64 char* progname
= baseleaf_from_fnm(argv
[0]);
65 for (char * p
= progname
; *p
; ++p
) {
66 *p
= tolower((unsigned char)*p
);
68 if (strcmp(progname
, "3dtopos") == 0) {
74 const int OPT_FMT_BASE
= 20000;
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
,
79 OPT_CENTRED
, OPT_FULL_COORDS
, OPT_CLAMP_TO_GROUND
, OPT_DEFAULTS
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'},
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
},
89 {"legs", no_argument
, 0, OPT_LEGS
},
90 {"surface-legs", no_argument
, 0, OPT_SURF
},
91 {"splays", no_argument
, 0, OPT_SPLAYS
},
92 {"crosses", no_argument
, 0, OPT_CROSSES
},
93 {"station-names", no_argument
, 0, OPT_LABELS
},
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
},
102 {"clamp-to-ground", no_argument
, 0, OPT_CLAMP_TO_GROUND
},
103 {"defaults", no_argument
, 0, OPT_DEFAULTS
},
104 {"grid", optional_argument
, 0, 'g'},
105 {"text-height", required_argument
, 0, 't'},
106 {"marker-size", required_argument
, 0, 'm'},
107 {"3d", no_argument
, 0, OPT_FMT_BASE
+ FMT_3D
},
108 {"csv", no_argument
, 0, OPT_FMT_BASE
+ FMT_CSV
},
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 {"shp-lines", no_argument
, 0, OPT_FMT_BASE
+ FMT_SHP_LINES
},
118 {"shp-points", no_argument
, 0, OPT_FMT_BASE
+ FMT_SHP_POINTS
},
119 {"svg", no_argument
, 0, OPT_FMT_BASE
+ FMT_SVG
},
120 {"help", no_argument
, 0, HLP_HELP
},
121 {"version", no_argument
, 0, HLP_VERSION
},
123 {"origin-in-center", no_argument
, 0, OPT_CENTRED
},
125 {"full-coords", no_argument
, 0, OPT_FULL_COORDS
},
129 #define short_opts "s:g::t:m:"
131 static struct help_msg help
[] = {
133 {HLP_ENCODELONG(0), /*only load the sub-survey with this prefix*/199, 0, 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, 0},
136 /* TRANSLATORS: These example input values should not be translated. */
137 {HLP_ENCODELONG(2), /*bearing (90, 90d, 100g all mean 90°)*/460, 0, 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, 0},
140 /* TRANSLATORS: Don't translate example command line option --tilt=-90 */
141 {HLP_ENCODELONG(4), /*plan view (equivalent to --tilt=-90)*/462, 0, 0},
142 /* TRANSLATORS: Don't translate example command line option --tilt=0 */
143 {HLP_ENCODELONG(5), /*elevation view (equivalent to --tilt=0)*/463, 0, 0},
144 {HLP_ENCODELONG(6), /*underground survey legs*/476, 0, 0},
145 {HLP_ENCODELONG(7), /*surface survey legs*/464, 0, 0},
146 {HLP_ENCODELONG(8), /*splay legs*/465, 0, 0},
147 {HLP_ENCODELONG(9), /*station markers*/474, 0, 0},
148 {HLP_ENCODELONG(10), /*station labels*/475, 0, 0},
149 {HLP_ENCODELONG(11), /*entrances*/466, 0, 0},
150 {HLP_ENCODELONG(12), /*fixed points*/467, 0, 0},
151 {HLP_ENCODELONG(13), /*exported stations*/468, 0, 0},
152 {HLP_ENCODELONG(14), /*cross-sections*/469, 0, 0},
153 {HLP_ENCODELONG(15), /*walls*/470, 0, 0},
154 {HLP_ENCODELONG(16), /*passages*/471, 0, 0},
155 {HLP_ENCODELONG(17), /*origin in centre*/472, 0, 0},
156 {HLP_ENCODELONG(18), /*full coordinates*/473, 0, 0},
157 {HLP_ENCODELONG(19), /*clamp to ground*/478, 0, 0},
158 {HLP_ENCODELONG(20), /*include items exported by default*/155, 0, 0},
159 {HLP_ENCODELONG(21), /*generate grid (default %sm)*/148, STRING(DEFAULT_GRID_SPACING
), 0},
160 {HLP_ENCODELONG(22), /*station labels text height (default %s)*/149, STRING(DEFAULT_TEXT_HEIGHT
), 0},
161 {HLP_ENCODELONG(23), /*station marker size (default %s)*/152, STRING(DEFAULT_MARKER_SIZE
), 0},
162 {HLP_ENCODELONG(24), /*produce Survex 3d output*/487, 0, 0},
163 {HLP_ENCODELONG(25), /*produce CSV output*/102, 0, 0},
164 {HLP_ENCODELONG(26), /*produce DXF output*/156, 0, 0},
165 {HLP_ENCODELONG(27), /*produce EPS output*/454, 0, 0},
166 {HLP_ENCODELONG(28), /*produce GPX output*/455, 0, 0},
167 {HLP_ENCODELONG(29), /*produce HPGL output*/456, 0, 0},
168 {HLP_ENCODELONG(30), /*produce JSON output*/457, 0, 0},
169 {HLP_ENCODELONG(31), /*produce KML output*/458, 0, 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, 0},
173 {HLP_ENCODELONG(33), /*produce Survex POS output*/459, 0, 0},
174 {HLP_ENCODELONG(34), /*produce Shapefile (lines) output*/525, 0, 0},
175 {HLP_ENCODELONG(35), /*produce Shapefile (points) output*/526, 0, 0},
176 {HLP_ENCODELONG(36), /*produce SVG output*/160, 0, 0},
182 string optmap
[sizeof(show_mask
) * CHAR_BIT
];
185 bool always_include_defaults
= false;
186 cmdline_init(argc
, argv
, short_opts
, long_opts
, &long_index
, help
, 1, 2);
189 int opt
= cmdline_getopt();
190 if (opt
== EOF
) break;
229 case OPT_FULL_COORDS
:
232 case OPT_CLAMP_TO_GROUND
:
233 bit
= CLAMP_TO_GROUND
;
236 always_include_defaults
= true;
240 grid
= cmdline_double_arg();
242 grid
= (double)DEFAULT_GRID_SPACING
;
247 char* colon
= strchr(optarg
, ':');
249 /* --scale=1000 => 1:1000 => scale = 1000 */
250 scale
= cmdline_double_arg();
252 /* --scale=0.001 => 1:1000 => scale = 1000 */
255 } else if (colon
- optarg
== 1 && optarg
[0] == '1') {
256 /* --scale=1:1000 => 1:1000 => scale = 1000 */
258 scale
= cmdline_double_arg();
261 /* --scale=2:1000 => 1:500 => scale = 500 */
263 scale
= cmdline_double_arg();
265 scale
= cmdline_double_arg() / scale
;
273 size_t len
= strlen(optarg
);
275 char ch
= optarg
[len
- 1];
280 optarg
[len
- 1] = '\0';
283 pan
= cmdline_double_arg();
284 optarg
[len
- 1] = ch
;
286 pan
= cmdline_double_arg();
296 size_t len
= strlen(optarg
);
298 char ch
= optarg
[len
- 1];
304 optarg
[len
- 1] = '\0';
307 tilt
= cmdline_double_arg();
308 optarg
[len
- 1] = ch
;
310 tilt
= cmdline_double_arg();
314 } else if (units
== '%') {
315 tilt
= deg(atan(tilt
* 0.01));
328 case 't': /* Text height */
329 text_height
= cmdline_double_arg();
332 case 'm': /* Marker size */
333 marker_size
= cmdline_double_arg();
339 filter
= new SurveyFilter();
348 if (opt
>= OPT_FMT_BASE
&& opt
< OPT_FMT_BASE
+ FMT_MAX_PLUS_ONE_
) {
349 format
= export_format(opt
- OPT_FMT_BASE
);
355 while (((bit
>> i
) & 1) == 0) ++i
;
357 if (!optmap
[i
].empty()) optmap
[i
] += ' ';
359 // Reconstruct what the command line option was.
360 if (long_index
< 0) {
362 optmap
[i
] += char(opt
);
364 if (optarg
== argv
[optind
- 1]) {
371 optmap
[i
] += long_opts
[long_index
].name
;
373 if (optarg
== argv
[optind
- 1]) {
384 // A single --survey is handled by img at load-time. Multiple --survey are
385 // handled via a SurveyFilter at export time.
386 if (filter
) survey
= NULL
;
388 const char* fnm_in
= argv
[optind
++];
389 const char* fnm_out
= argv
[optind
];
391 if (format
== FMT_MAX_PLUS_ONE_
) {
392 // Select format based on extension.
393 size_t len
= strlen(fnm_out
);
394 // Length of longest extension of interest.
395 constexpr size_t MAX_EXT_LEN
= 4;
396 char ext
[MAX_EXT_LEN
+ 2];
397 for (size_t i
= 0; i
< MAX_EXT_LEN
+ 2; ++i
) {
398 ext
[i
] = tolower((unsigned char)fnm_out
[len
- (MAX_EXT_LEN
+ 1) + i
]);
400 for (size_t i
= 0; i
< FMT_MAX_PLUS_ONE_
; ++i
) {
401 const auto& info
= export_format_info
[i
];
402 size_t l
= strlen(info
.extension
);
404 strcmp(ext
+ MAX_EXT_LEN
+ 1 - l
, info
.extension
) == 0) {
405 // Shapefile (lines) will be selected for .shp, which is
406 // probably what's wanted.
407 format
= export_format(i
);
411 if (format
== FMT_MAX_PLUS_ONE_
) {
412 fatalerror(/*Export format not specified and not known from output file extension*/252);
416 if (format
== FMT_MAX_PLUS_ONE_
) {
417 fatalerror(/*Export format not specified*/253);
419 char *baseleaf
= baseleaf_from_fnm(fnm_in
);
420 /* note : memory allocated by fnm_out gets leaked in this case... */
421 fnm_out
= add_ext(baseleaf
, export_format_info
[format
].extension
);
425 const auto& format_info_mask
= export_format_info
[format
].mask
;
426 unsigned not_allowed
= show_mask
&~ format_info_mask
;
428 printf("warning: The following options are not supported for this export format and will be ignored:\n");
431 while (not_allowed
) {
432 if (not_allowed
& bit
) {
433 // E.g. --walls maps to two bits in show_mask, but the options
434 // are only put on the least significant in such cases.
435 if (!optmap
[i
].empty())
436 printf("%s\n", optmap
[i
].c_str());
442 show_mask
&= format_info_mask
;
445 if (always_include_defaults
|| show_mask
== 0) {
446 show_mask
|= export_format_info
[format
].defaults
;
449 if (!(format_info_mask
& ORIENTABLE
)) {
455 int err
= model
.Load(fnm_in
, survey
);
456 if (err
) fatalerror(err
, fnm_in
);
457 if (filter
) filter
->SetSeparator(model
.GetSeparator());
460 if (!Export(fnm_out
, model
.GetSurveyTitle(),
461 model
.GetDateString(),
463 pan
, tilt
, show_mask
, format
,
464 grid
, text_height
, marker_size
,
466 fatalerror(/*Couldn’t write file “%s”*/402, fnm_out
);
468 } catch (const wxString
& m
) {
469 wxString r
= msg_appname();
471 r
+= wmsg(/*error*/93);
474 wcerr
<< r
.c_str() << '\n';