Kjører mergesvn på alle filene i /trunk/ (ikke underkataloger). Det er
[gpstools.git] / branches / gpst.near / gpst
blobca27c96d964a75b0134ef96616d48f572c407c40
1 #!/usr/bin/perl -w
3 #=======================================================================
4 # $Id$
5 # Converts between various GPS formats
7 # Character set: UTF-8
8 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
9 # License: GNU General Public License, see end of file for legal stuff.
10 #=======================================================================
12 use strict;
13 use Getopt::Long;
14 use Time::Local qw { timegm_nocheck };
16 BEGIN {
17 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
18 our @version_array;
21 use GPST;
22 use GPSTdate;
23 use GPSTdebug;
24 use GPSTgeo;
25 use GPSTxml;
27 $| = 1;
29 our $Debug = 0;
31 our %Opt = (
32 # Initial values for command line arguments {{{
33 'chronology' => 0,
34 'comment-out-dups' => 0,
35 'create-breaks' => 0,
36 'debug' => 0,
37 'double-y-scale' => 0,
38 'epoch' => 0,
39 'fix' => 0,
40 'help' => 0,
41 'inside' => 0,
42 'near' => "",
43 'output-format' => "gpsml",
44 'outside' => 0,
45 'pos1' => "",
46 'pos2' => "",
47 'require' => "",
48 'round' => "",
49 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
50 'short-date' => 0,
51 'skip-dups' => 0,
52 'strip-whitespace' => 0,
53 'undefined' => "",
54 'version' => 0,
55 # }}}
58 our $progname = $0;
59 $progname =~ s#^.*/(.*?)$#$1#;
61 my $rcs_id = '$Id$';
62 my $id_date = $rcs_id;
63 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
65 push(@main::version_array, $rcs_id);
67 Getopt::Long::Configure("bundling");
68 GetOptions(
69 # Command line options {{{
70 "chronology" => \$Opt{'chronology'},
71 "comment-out-dups|u" => \$Opt{'comment-out-dups'},
72 "create-breaks|t" => \$Opt{'create-breaks'},
73 "debug" => \$Opt{'debug'},
74 "double-y-scale|y" => \$Opt{'double-y-scale'},
75 "epoch|e" => \$Opt{'epoch'},
76 "fix" => \$Opt{'fix'},
77 "help|h" => \$Opt{'help'},
78 "inside" => \$Opt{'inside'},
79 "near" => \$Opt{'near'},
80 "output-format|o=s" => \$Opt{'output-format'},
81 "outside" => \$Opt{'outside'},
82 "pos1=s" => \$Opt{'pos1'},
83 "pos2=s" => \$Opt{'pos2'},
84 "require|r=s" => \$Opt{'require'},
85 "round|R=s" => \$Opt{'round'},
86 "save-to-file|S=s" => \$Opt{'save-to-file'},
87 "short-date|s" => \$Opt{'short-date'},
88 "skip-dups|d" => \$Opt{'skip-dups'},
89 "strip-whitespace|w" => \$Opt{'strip-whitespace'},
90 "undefined|n=s" => \$Opt{'undefined'},
91 "verbose|v" => \$Opt{'verbose'},
92 "version" => \$Opt{'version'},
93 # }}}
94 ) || die("$progname: Option error. Use -h for help.\n");
96 my %Dat;
98 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
99 my $Udef = "?";
100 our $DIGIT = '[0-9\.\-\+]'; # Used in regexps
101 $GPST::Spc = $Opt{'strip-whitespace'} ? "" : " ";
102 my $Spc = $GPST::Spc; # FIXME
103 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
104 my $first_time = 0;
105 my $last_time = 0;
106 my ($last_lon, $last_lat, $last_line) =
107 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
108 my ($lat1, $lon1, $lat2, $lon2) =
109 (-1000, -1000, 1000, 1000);
111 our %Cmd = (
112 'gpsbabel' => '/usr/local/bin/gpsbabel',
115 my %Poscount = ();
117 my %Req = (
118 'ele' => ($Opt{'require'} =~ /e/) ? 1 : 0,
119 'position' => ($Opt{'require'} =~ /p/) ? 1 : 0,
120 'time' => ($Opt{'require'} =~ /t/) ? 1 : 0,
122 $Opt{'require'} =~ /[^ept]/
123 && die("$0: Unknown flag in --require (-r) value\n");
125 $Opt{'debug'} && ($Debug = 1);
126 $Opt{'help'} && usage(0);
127 $Opt{'version'} && print_version();
129 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
130 $lat1 = $1;
131 $lon1 = $2;
133 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
134 $lat2 = $1;
135 $lon2 = $2;
137 if ($lat1 > $lat2) {
138 my $Tmp = $lat1;
139 $lat1 = $lat2;
140 $lat2 = $Tmp;
142 if ($lon1 > $lon2) {
143 my $Tmp = $lon1;
144 $lon1 = $lon2;
145 $lon2 = $Tmp;
148 if ($Opt{'inside'} && $Opt{'outside'}) {
149 die("$progname: Cannot mix the --inside and --outside options\n");
152 # To avoid printing out extra "/> at the start of svg output:
153 my $svg_start_thing = "";
155 my %Round = ();
157 if (defined($Opt{'round'})) {
158 my $R = $Opt{'round'};
159 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
162 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
163 # Kunne vært et eget script på grunn av at det gjør sine helt egne
164 # greier, men like greit å samle det på en plass.
165 # FIXME: Fjerner ikke første duplikatentryen.
166 # FIXME: Se om det går å få flytta den inn i print_entry() så man
167 # slipper å ha to gptrans_conv’er i pipen.
168 # FIXME: Legg inn alle formatene.
169 if ($Opt{'comment-out-dups'}) {
170 # Comment out areas without reception {{{
171 my ($start_date, $end_date, $found_dup) = ("", "", 0);
172 my @Dup = ();
173 while (<>) {
174 if (
176 1\x20
177 (\S+)\x20 # Lat
178 (\S+)\x20 # Lon
179 (\S+)\x20 # Speed
180 (\S+)\x20 # Unknown
181 (\d\d)/(\d\d)/(\d\d\d\d)\x20 # Month/Day/Year — urgh
182 (\d\d):(\d\d):(\d\d) # Hour:Min:Sec
185 # {{{
186 my ($lat_val, $lon_val, $Speed, $Unkn,
187 $Month, $Day, $Year, $Hour, $Min, $Sec) =
188 ($1, $2, $3, $4,
189 $5, $6, $7, $8, $9, $10);
190 if (($lat_val eq $last_lat) && ($lon_val eq $last_lon)) {
191 unless ($found_dup) {
192 $start_date = "$Year$Month${Day}T$Hour$Min$Sec";
193 @Dup = ();
194 $found_dup = 1;
196 push(@Dup, "# $_");
197 $end_date = "$Year$Month${Day}T$Hour$Min$Sec";
198 } else {
199 if ($found_dup) {
200 print("# $start_date-$end_date: " .
201 "CO: No signal \x7B\x7B\x7B\n");
202 for (@Dup) {
203 print($_);
205 print("# $start_date-$end_date: " .
206 "CO: No signal \x7D\x7D\x7D\n# move\n$_");
207 $found_dup = 0;
208 } else {
209 print($_);
212 $last_lat = $lat_val;
213 $last_lon = $lon_val;
214 # }}}
215 } else {
216 if ($found_dup) {
217 push(@Dup, $_);
218 } else {
219 print($_);
223 if ($found_dup) {
224 print("# $start_date-$end_date: " .
225 "CO: No signal \x7B\x7B\x7B\n");
226 for (@Dup) {
227 print($_);
229 print("# $start_date-$end_date: " .
230 "CO: No signal \x7D\x7D\x7D\n# move\n");
231 $found_dup = 0;
233 exit(0);
234 # }}}
237 $Opt{'save-to-file'} eq "\n" && print_header(*STDOUT);
239 my @first_lines;
240 my $xml_data;
241 my $data_line = "";
242 my $curr_file = "";
244 my $from_stdin = scalar(@ARGV) ? 0 : 1;
246 $from_stdin && push(@ARGV, "-");
248 for $curr_file (@ARGV) {
249 # Scan through stdin or specified files and send every GPS entry to
250 # print_entry()
251 # {{{
252 print(STDERR "$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
253 if (open(CurrFP, "<$curr_file")) {
254 # {{{
255 while (<CurrFP>) {
256 $data_line = $_;
257 %Dat = (
258 'year' => '', 'month' => '', 'day' => '',
259 'hour' => '', 'min' => '', 'sec' => '',
260 'lat' => '', 'lon' => '',
261 'ele' => '',
262 'desc' => '',
263 'error' => "",
264 'type' => 'tp',
267 if ($Opt{'save-to-file'} ne "\n") {
268 push(@first_lines, $_);
270 s/^# error // && ($Dat{'error'} = "error");
271 s/^# ?// && ($Dat{'error'} = "desc");
272 $xml_data = "";
273 if (m#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
274 # gpsml — The main storage format {{{
275 my ($Elem, $Props, $Data) =
276 ( $1, $2, $3);
277 my $err_str = ($Props =~ /\berr="(.*?)"/) ? $1 : "error";
278 $Elem eq "etp" && ($Dat{'error'} = $err_str);
279 my $Time = "";
280 $Data =~ m#<time>(.*?)</time># && ($Time = $1);
281 $Time =~ s{
282 (\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?([\d\.]+?)Z
284 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
285 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
286 ( $1, $2, $3,
287 $4, $5, $6);
289 }ex;
290 $Data =~ m#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
291 $Data =~ m#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
292 $Data =~ m#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
293 $Data =~ m#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
294 print_entry(%Dat);
295 # }}}
296 } elsif (m#^<break\b.*?/>#) {
297 $found_move = 1;
298 } elsif (m#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
299 $Dat{'type'} = $1;
300 $Dat{$1} = $2;
301 print_entry(%Dat);
302 } elsif (m#^<desc\b.*?>(.*$)#s) {
303 $Dat{'type'} = "desc";
304 my $Txt = $1;
305 until ($Txt =~ m#</desc>#s) {
306 $Txt .= <CurrFP>;
308 $Txt =~ s#^(.*)(</desc>.*$)#$1#s;
309 $Dat{'desc'} = $Txt;
310 print_entry(%Dat);
311 } elsif (/<gpx\b/) {
312 $xml_data = $_;
313 $xml_data .= join("", <CurrFP>);
314 if (!length($Opt{'output-format'})) {
315 $Opt{'output-format'} = "gpx";
316 print_header(*STDOUT);
318 read_xmlfile($xml_data);
319 last;
320 } elsif (/^move$/) {
321 $found_move = 1;
322 } elsif (m#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
323 # CSV format, epoch style {{{
324 my ($ep_time, $lon_val, $lat_val, $Alt) =
325 ( $1, $2, $3, $4);
326 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
327 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
328 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
329 $Dat{'month'}++; # Urgh Ⅰ
330 $Dat{'year'} += 1900; # Urgh Ⅱ
331 print_entry(%Dat);
332 # }}}
333 } elsif (
335 (\d\d\d\d)-?(\d\d)-?(\d\d)[T\ ](\d\d):?(\d\d):?(\d\d)Z?\t
336 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
339 # CSV format, human-readable date format {{{
340 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
341 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
342 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
343 ($1, $2, $3,
344 $4, $5, $6,
345 $7, $8, $9);
346 print_entry(%Dat);
347 # }}}
348 } elsif (/^Trackpoint\t/) {
349 # Trackpoint\tN60.41630 E5.31675\t09.02.2006 20:24:37 (UTC)\t13.6 m\t\t93.9 m\t00:00:06\t56 kph\t123° true {{{
351 # Trackpoint\t
352 # N60.41630 E5.31675\t
353 # 09.02.2006 20:24:37 (UTC)\t
354 # 13.6 m\t
355 # \t
356 # 93.9 m\t
357 # 00:00:06\t
358 # 56 kph\t
359 # 123° true
360 my $Orig = $_;
361 $Orig =~ s/[\r\n]+$//;
362 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
363 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
364 split(/\t/, $Orig .
365 # Nødløsning for å unngå at variabler
366 # blir udefinert.
367 "\t\t\t\t\t\t\t\t\t\t"
369 D(join("",
370 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
371 "Time_f=\"$Time_f\"\n",
372 "Alt_f=\"$Alt_f\"\n",
373 "Depth_f=\"$Depth_f\"\n",
374 "Leglength_f=\"$Leglength_f\"\n",
375 "Legtime_f=\"$Legtime_f\"\n",
376 "Legspeed_f=\"$Legspeed_f\"\n",
377 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
379 my ($NS, $WE,
380 $Alt_unit,
381 $Leglength,
382 $Legtime_hour, $Legtime_min, $Legtime_sec,
383 $Legspeed, $Legspeed_unit,
384 $Legcourse
385 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
386 "", "", "", "", "", "", "", "", "", "");
387 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
388 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
389 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
390 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
391 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
392 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
393 ($Dat{'ele'} = $1, $Alt_unit = $2);
394 D("ele = \"$Dat{'ele'}\"");
395 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
396 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
397 # MapSource in win xp writes YYYY, but YY in win98se.
399 defined($Dat{'year'})
400 && $Dat{'year'} =~ /\d/
401 && $Dat{'year'} < 1900
402 ) && ($Dat{'year'} += 2000);
403 print_entry(%Dat);
404 # }}}
405 } elsif (/^Track\t(.*?)\t/) {
406 $Dat{'title'} = txt_to_xml($1);
407 $Dat{'type'} = "title";
408 $found_move = 1;
409 print_entry(%Dat);
410 } elsif (
413 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)\t
414 (.+)\xB0(.+)'(.+)"\t
415 (.+)\xB0(.+)'(.+)"
418 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
419 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
420 ($Dat{'month'}, $Dat{'day'}, $Dat{'year'},
421 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
422 $lat_d, $lat_m, $lat_s,
423 $lon_d, $lon_m, $lon_s) =
424 ($1, $2, $3,
425 $4, $5, $6,
426 $7, $8, $9,
427 $10, $11, $12);
428 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
429 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
430 $Dat{'lat'} = sprintf("%${Flat}f",
431 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
432 $Dat{'lon'} = sprintf("%${Flon}f",
433 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
434 print_entry(%Dat);
435 # }}}
436 } elsif (
438 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
439 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
442 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
443 ($Dat{'lat'}, $Dat{'lon'}, $Dat{'speed'},
444 $Dat{'unkn'},
445 $Dat{'month'}, $Dat{'day'}, $Dat{'year'},
446 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
447 ($1, $2, $3,
449 $5, $6, $7,
450 $8, $9, $10);
451 print_entry(%Dat);
452 # }}}
453 } elsif (/^
454 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
455 # Regexp {{{
456 (@) # @
457 (\d\d) # Year
458 (\d\d) # Month
459 (\d\d) # Day
460 (\d\d) # Hours
461 (\d\d) # Minutes
462 (\d\d) # Seconds
463 ([NS]) # N|S
464 (\d\d) # Latitude degree
465 (\d\d) # Latitude minute
466 (\d\d\d) # Latitude minute decimals
467 ([EW]) # E|W
468 (\d\d\d) # Longitude degree
469 (\d\d) # Longitude minute
470 (\d\d\d) # Longitude minute degree
471 (....) # Accurancy
472 (......) # Elevation
473 (...............)
474 # }}}
475 /x) {
476 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
477 $lon_degmin, $lon_mindec);
478 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'}, $Dat{'hour'},
479 $Dat{'min'}, $Dat{'sec'}, $NS, $lat_deg,
480 $lat_degmin, $lat_mindec, $EW,
481 $lon_deg, $lon_degmin, $lon_mindec,
482 $Dat{'accur'}, $Dat{'ele'}, $Dat{'unknown'}) =
483 ($2+2000, $3, $4, $5,
484 $6, $7, $8, $9,
485 $10, $11, $12,
486 $13, $14, $15,
487 $16, $17, $18);
488 my $ep_time = timegm_nocheck(
489 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
490 $Dat{'day'}, $Dat{'month'}-1, $Dat{'year'}
492 $last_time = $ep_time;
493 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
494 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
495 my $tmp_lon = sprintf(
496 "%${Flon}f",
497 $lon_deg +
498 $lon_degmin/60 +
499 $lon_mindec/60000);
500 my $tmp_lat = sprintf("%${Flat}f",
501 $lat_deg +
502 $lat_degmin/60 +
503 $lat_mindec/60000);
504 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
505 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
506 $Dat{'lat'} = $tmp_lat;
507 $Dat{'lon'} = $tmp_lon;
508 print_entry(%Dat);
509 # }}}
510 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
511 # @020721221336__________________________________________ {{{
512 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
513 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'rest'}) =
514 ($2+2000, $3, $4,
515 $5, $6, $7, $8);
516 $Dat{'error'} = "nosignal";
517 print_entry(%Dat);
518 # }}}
519 } elsif (/^xmaplog /) {
520 ($Opt{'output-format'} eq "csv")
521 && ($Opt{'save-to-file'} eq "\n")
522 && print("\n");
523 } elsif (/^$/) {
524 ($Opt{'output-format'} eq "csv")
525 && ($Opt{'save-to-file'} eq "\n")
526 && print("\n");
527 } elsif (/^Pause: /) {
528 # NOP, is here to cope with old files I’ve lying around.
529 } elsif ($Dat{'error'} eq "desc") {
530 my $Comment = $_;
531 if (defined($Comment)) {
532 $Comment =~ s/^\s*(.*?)\s*$/$1/;
533 if ($Opt{'output-format'} eq "gpsml") {
534 $Dat{'desc'} = txt_to_xml($Comment);
535 $Dat{'type'} = "desc";
536 print_entry(%Dat);
539 } else {
540 $Opt{'verbose'} && warn("Line $.: Unknown: \"$_\"\n");
543 # }}}
544 } else {
545 warn("$progname: $curr_file: Cannot open file for read: $!\n");
547 # }}}
550 print_footer(*STDOUT);
552 exit(0);
554 sub read_xmlfile {
555 # {{{
556 my $Txt = join("", @_);
557 $Txt =~ s/<!--(.*?)-->//gs;
558 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
559 # }}}
562 sub print_gpx {
563 # {{{
564 my $Orig = shift;
565 my $Str = $Orig;
566 D("print_xml_gps(\"$Orig\")\n");
567 $Str =~ s/<!--(.*?)-->//gs;
568 $Str =~
570 <trk\b(.*?)>(.*?)</trk>
573 my $el_trk = $2;
574 $el_trk =~
576 <name\b(.*?)>(.*?)</name>
578 my %tmp_dat = ();
579 $tmp_dat{'title'} = $2;
580 $tmp_dat{'type'} = "title";
581 print_entry(%tmp_dat);
583 }sex;
584 $el_trk =~
586 <trkseg\b(.*?)>(.*?)</trkseg>
589 my $el_trkseg = $2;
590 $el_trkseg =~
592 <trkpt\b(.*?)>(.*?)</trkpt>
595 my ($attr_trkpt, $el_trkpt) =
596 ( $1, $2);
597 %Dat = (
598 'year' => '', 'month' => '', 'day' => '',
599 'hour' => '', 'min' => '', 'sec' => '',
600 'lat' => '', 'lon' => '',
601 'ele' => '',
602 'desc' => '',
603 'error' => "",
604 'type' => 'tp',
606 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon'} = $1);
607 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat'} = $1);
608 ($el_trkpt =~ m#<($wpt_elems)\b.*?>(.*?)</($wpt_elems)>#)
609 && ($Dat{$1} = $2);
610 if (
611 $el_trkpt =~
613 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
614 (\d\d):?(\d\d):?([\d\.]+)Z</time>
617 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
618 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
619 ($1, $2, $3, $4, $5, $6);
621 print_entry(%Dat);
623 }gsex;
624 $found_move = 1;
625 }gsex;
626 $found_move = 1;
627 }gsex;
628 # }}}
631 sub print_header {
632 # {{{
633 local *OutFP = shift;
634 if ($Opt{'output-format'} eq "gpsml") {
635 print(OutFP join("",
636 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
637 "<gpsml>\n",
638 "<track>\n",
640 } elsif ($Opt{'output-format'} eq "gpstrans") {
641 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
642 "Datum[100]: WGS 84\n");
643 } elsif ($Opt{'output-format'} eq "gpx") {
644 print(OutFP join("",
645 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
646 qq{<gpx\n},
647 qq{$Spc${Spc}version="1.1"\n},
648 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
649 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
650 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
651 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
652 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
653 qq{>\n},
654 qq{$Spc$Spc<trk>\n},
655 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
657 } elsif ($Opt{'output-format'} eq "ps") {
658 print(OutFP ps_header(532, 6034, 533, 6040));
659 print(OutFP "*u\n");
660 } elsif ($Opt{'output-format'} eq "svg") {
661 print(OutFP join("",
662 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
663 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
664 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
665 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
666 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
667 "$Spc$Spc<title></title>\n",
668 "$Spc$Spc<desc></desc>\n",
671 # }}}
674 sub print_footer {
675 # Print footer {{{
676 local *OutFP = shift;
677 if ($Opt{'output-format'} eq "gpsml") {
678 print(OutFP join("",
679 "</track>\n",
680 "</gpsml>\n",
682 } elsif ($Opt{'output-format'} eq "gpx") {
683 print(OutFP join("",
684 "$Spc$Spc$Spc$Spc</trkseg>\n",
685 "$Spc$Spc</trk>\n",
686 "</gpx>\n",
688 } elsif ($Opt{'output-format'} eq "poscount") {
689 while (my ($l_name, $l_val) = each %Poscount) {
690 $l_name =~ /^(.+?),(.+?)$/
691 && print(OutFP "$1\t$2\t$l_val\n");
693 } elsif ($Opt{'output-format'} eq "ps") {
694 print(OutFP join("",
695 "*U\n",
696 "%%Trailer\n",
697 "%%EOF\n",
699 } elsif ($Opt{'output-format'} eq "svg") {
700 print(OutFP "\"/>\n</svg>\n");
702 # }}}
705 sub print_entry {
706 # Print a GPS entry with time, latitude, longitude and elevation in
707 # various formats
708 # {{{
709 my %Dat = @_;
710 defined($Dat{'desc'}) || ($Dat{'desc'} = "");
711 defined($Dat{'ele'}) || ($Dat{'ele'} = "");
712 defined($Dat{'lat'}) || ($Dat{'lat'} = "");
713 defined($Dat{'lon'}) || ($Dat{'lon'} = "");
714 defined($Dat{'year'}) || ($Dat{'year'} = "");
715 my $print_time = length($Dat{'year'}) ? 1 : 0;
716 my $print_pos = (length($Dat{'lat'}) && length($Dat{'lon'})) ? 1 : 0;
717 if (!$print_pos) {
718 $Dat{'lat'} = $Dat{'lon'} = "";
720 my $print_ele = length($Dat{'ele'}) ? 1 : 0;
721 my $print_desc = length($Dat{'desc'}) ? 1 : 0;
722 my $Line = "";
723 D("print_entry(\"" . join("\", \"", @_) . "\");");
724 my $ep_time;
726 if ($Opt{'near'} && $print_pos) {
727 $Dat{'extensions'} .= sprintf("%s ",
728 list_nearest_waypoints($Dat{'lat'}, $Dat{'lon'}, 3));
731 if (length($Opt{'round'})) {
732 for my $Tmp (qw{ lat lon ele }) {
733 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
734 D("Tmp = '$Tmp'");
735 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
740 if ($Opt{'output-format'} eq "poscount") {
741 my $Name = "$Dat{'lon'},$Dat{'lat'}";
742 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
743 $Poscount{$Name}++;
744 return;
747 if ($print_time) {
748 $ep_time = timegm_nocheck(
749 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
750 $Dat{'day'}, $Dat{'month'} - 1, $Dat{'year'}
752 $Dat{'year'} = sprintf("%04u", $Dat{'year'});
753 $Dat{'month'} = sprintf("%02u", $Dat{'month'});
754 $Dat{'day'} = sprintf("%02u", $Dat{'day'});
755 $Dat{'hour'} = sprintf("%02u", $Dat{'hour'});
756 $Dat{'min'} = sprintf("%02u", $Dat{'min'});
757 $Dat{'sec'} = sprintf("%02u", $Dat{'sec'});
758 if ($Opt{'chronology'}) {
759 if ($last_time > $ep_time && !length($Dat{'error'})) {
760 warn(sprintf(
761 "%s: \"%sZ\": Next date is %s in the past (%sZ)\n",
762 $progname, sec_to_string($last_time, "T"),
763 sec_to_readable($last_time-$ep_time),
764 sec_to_string($ep_time, "T")
766 # FIXME: Make --fix work with gpx.
767 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^gpx$/)) {
768 $Dat{'error'} = "chrono";
772 } else {
773 $ep_time = 0;
774 $Dat{'year'} = 0;
775 $Dat{'month'} = 0;
776 $Dat{'day'} = 0;
777 $Dat{'hour'} = 0;
778 $Dat{'min'} = 0;
779 $Dat{'sec'} = 0;
782 if ($Opt{'save-to-file'} ne "\n") {
783 # {{{
784 $print_time || return;
785 my $base_name = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
786 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z" .
787 "$Opt{'save-to-file'}";
788 my $file_name = $base_name;
789 if (-e $file_name) {
790 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
791 $file_name = "$base_name.dup_$a";
793 if (-e $file_name) {
794 die("$progname: $base_name: File already exists, and ran " .
795 "out of attempts to create unique file name\n");
797 if ($Opt{'verbose'}) {
798 warn("$progname: $base_name: File already exists, using " .
799 "unique name \"$file_name\" instead\n");
802 if (open(ToFP, ">", $file_name)) {
803 print_header(*ToFP);
804 print(ToFP (
805 $from_stdin
806 ? @first_lines
807 : ()),
808 (length($xml_data)
809 ? $xml_data
810 : <>)
811 ) || die("$progname: $file_name: Cannot write to file: $!\n");
812 print_footer(*ToFP);
813 close(ToFP);
814 if ($Opt{'output-format'} eq "gpsml") {
815 printf("<include>%s</include>\n",
816 txt_to_xml($file_name));
817 } elsif ($Opt{'output-format'} eq "gpx") {
818 printf("<!-- Saved unconverted data to \"%s\" -->\n",
819 txt_to_xml($file_name));
820 } else {
821 print("$progname: Saved unconverted data to \"$file_name\"\n");
823 exit 0;
824 } else {
825 die("$progname: $file_name: Cannot create file: $!\n");
827 # }}}
830 my $pause_len = 0;
831 my $do_print = 1;
833 if ($Dat{'type'} eq "tp") {
834 # {{{
835 if ($Opt{'require'}) {
836 $Req{'time'} && !$print_time && return;
837 $Req{'position'} && !$print_pos && return;
838 $Req{'ele'} && !$print_ele && return;
841 if ($Opt{'inside'} || $Opt{'outside'}) {
842 if (
843 ($Dat{'lat'} < $lat1) ||
844 ($Dat{'lat'} > $lat2) ||
845 ($Dat{'lon'} < $lon1) ||
846 ($Dat{'lon'} > $lon2)
848 $Opt{'inside'} && return;
849 } else {
850 $Opt{'outside'} && return;
854 if ($Opt{'output-format'} eq "ps") {
855 $Dat{'lon'} *= 100;
856 $Dat{'lat'} *= 100;
859 if (
860 $Opt{'skip-dups'}
861 && ($Dat{'lon'} eq $last_lon)
862 && ($Dat{'lat'} eq $last_lat)
864 $Dat{'error'} = "dup";
865 } else {
866 $do_print = 1;
869 if (
870 $Opt{'create-breaks'}
871 && $ep_time-$last_time > $PAUSE_LIMIT
872 && $last_time
874 $pause_len = $ep_time-$last_time;
875 D("pause_len set to '$pause_len'");
878 if ($pause_len) {
879 if ($Opt{'output-format'} eq "gpsml") {
880 $Line .= sprintf("<pause>%s</pause>\n",
881 sec_to_readable($ep_time-$last_time));
882 } elsif ($Opt{'output-format'} eq "csv") {
883 $Line .= sprintf("# Pause: %s\n# move\n",
884 sec_to_readable($ep_time-$last_time));
887 # }}}
890 if ($do_print) {
891 # Valid data was found, send to stdout {{{
892 unless ($first_time) {
893 $first_time = $ep_time;
895 if ($Opt{'double-y-scale'}) {
896 $Dat{'lat'} *= 2;
898 if ($Opt{'output-format'} eq "gpsml") {
899 if ($Dat{'type'} eq "tp") {
900 $Dat{'format'} = "gpsml";
901 $Line .= trackpoint(%Dat);
902 } elsif ($Dat{'type'} =~ /^(pause|desc|title)$/) {
903 $Line .= sprintf("<%s>%s</%s>\n",
905 $Dat{$1},
906 $1);
908 } elsif ($Opt{'output-format'} eq "xgraph") {
909 $pause_len && ($Line .= "move ");
910 ($Line .= "$Dat{'lon'} $Dat{'lat'}\n");
911 } elsif($Opt{'output-format'} eq "gpstrans") {
912 my ($gpt_lat, $gpt_lon) =
913 (ddd_to_dms($Dat{'lat'}), ddd_to_dms($Dat{'lon'}));
914 if ($print_time) {
915 $Line .= "T\t$Dat{'month'}/$Dat{'day'}/$Dat{'year'} " .
916 "$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}\t" .
917 "$gpt_lat\t$gpt_lon\n";
918 } else {
919 $Line .= "T\t00/00/00 00:00:00\t$gpt_lat\t$gpt_lon\n";
921 } elsif($Opt{'output-format'} eq "gpx") {
922 if ($Dat{'type'} eq "tp") {
923 $Dat{'format'} = "gpx";
924 $Line .= trackpoint(%Dat);
926 } elsif ($Opt{'output-format'} eq "clean") {
927 $pause_len && ($Line .= "\n");
928 ($Line .= "$Dat{'lon'}\t$Dat{'lat'}" .
929 ($print_ele
930 ? "\t$Dat{'ele'}"
931 : "") .
932 "\n");
933 } elsif ($Opt{'output-format'} eq "ps") {
934 $Line .= (
935 $pause_len
936 ? "f\n$Dat{'lon'} $Dat{'lat'} m\n"
937 : "$Dat{'lon'} $Dat{'lat'} l\n"
939 } elsif ($Opt{'output-format'} eq "svg") {
940 $Line .= (
941 ($last_lon == 1000) || $pause_len
942 ? join("",
943 "$svg_start_thing<path\n",
944 " stroke=\"blue\"\n",
945 " stroke-width=\"0.001\"\n",
946 " fill=\"none\"\n",
947 " d=\"\n",
948 "M $Dat{'lon'} $Dat{'lat'}\n")
949 : "L $Dat{'lon'} $Dat{'lat'}\n"
951 } elsif ($Opt{'output-format'} eq "ygraph") {
952 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
953 $Line .= "\"Time = $Time.0\n$Dat{'lon'} $Dat{'lat'}\n\n";
954 } elsif ($Opt{'output-format'} eq "csv") {
955 # {{{
956 # $do_print || print("skipping ");
957 $Line .= join("\t",
958 $print_time
959 ? $Opt{'epoch'}
960 ? $ep_time
961 : $Opt{'short-date'}
962 ? "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
963 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z"
964 : "$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T" .
965 "$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z"
966 : "",
967 $Dat{'lon'},
968 $Dat{'lat'},
969 $print_ele ? $Dat{'ele'} : "", # Elevation
970 "\n"
972 # }}}
973 } else {
974 die("$progname: \"$Opt{'output-format'}\": " .
975 "Unknown output format\n");
977 # }}}
980 if (!$last_time && $Opt{'output-format'} eq "ps") {
981 $Line .= "$Dat{'lon'} $Dat{'lat'} m\n";
984 if ($do_print) {
985 if ($found_move) {
986 if ($Opt{'output-format'} eq "gpsml") {
987 $Line = "<break/>\n$Line";
989 (!$pause_len && ($Opt{'output-format'} eq "xgraph"))
990 && ($Line .= "move $Line");
991 ($Opt{'output-format'} eq "clean") && ($Line .= "\n");
992 if ($Opt{'output-format'} eq "gpx") {
993 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
994 "$Spc$Spc$Spc$Spc<trkseg>\n";
996 $found_move = 0;
998 print($Line);
1000 $print_time && ($last_time = $ep_time);
1001 if ($print_pos) {
1002 $last_lon = $Dat{'lon'};
1003 $last_lat = $Dat{'lat'};
1005 $last_line = $data_line;
1006 $svg_start_thing = "\"/>\n";
1007 # }}}
1010 sub ps_header {
1011 # Send a Postscript header to stdout {{{
1012 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1013 my $Date = sec_to_string(time);
1014 return(join("",
1015 "%!PS-Adobe-3.0 EPSF-3.0\n",
1016 "%%Creator: $rcs_id\n",
1017 "%%Title:\n",
1018 "%%CreationDate: $Date\n",
1019 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1020 "%%DocumentData: Clean7Bit\n",
1021 "%%EndComments\n",
1022 "%%BeginProlog\n",
1023 "/bd { bind def } bind def\n",
1024 "/incompound false def\n",
1025 "/m { moveto } bd\n",
1026 "/l { lineto } bd\n",
1027 "/c { curveto } bd\n",
1028 "/F { incompound not {fill} if } bd\n",
1029 "/f { closepath F } bd\n",
1030 "/S { stroke } bd\n",
1031 "/*u { /incompound true def } bd\n",
1032 "/*U { /incompound false def f} bd\n",
1033 "/k { setcmykcolor } bd\n",
1034 "/K { k } bd\n",
1035 "%%EndProlog\n",
1036 "%%BeginSetup\n",
1037 "%%EndSetup\n",
1039 # }}}
1042 sub print_version {
1043 # Print program version {{{
1044 for (@main::version_array) {
1045 print("$_\n");
1047 exit(0);
1048 # }}}
1049 } # print_version()
1051 sub usage {
1052 # Send the help message to stdout {{{
1053 my $Retval = shift;
1055 print(<<END);
1057 $rcs_id
1059 Converts between various GPS formats.
1061 Usage: $progname [options] [file [files [...]]]
1062 $progname -S [file [files [...]]]
1063 $progname -u [file [files [...]]]
1065 Options:
1067 --chronology
1068 Check for broken chronology, warn about entries with an old
1069 timestamp.
1070 -d, --skip-dups
1071 Skip duplicated coordinates.
1072 -e, --epoch
1073 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1074 --fix
1075 Comment out entries which is obviously wrong. Use together with
1076 --chronology to fix those kind of errors. Does not work with GPX
1077 output yet.
1078 -h, --help
1079 Show this help.
1080 --inside
1081 Print only trackpoints inside a rectangle specified by --pos1 and
1082 --pos2.
1083 -n x, --undefined x
1084 Use x as undefined value. Default: "$Udef".
1085 --near
1086 Add names of the three closest waypoints to the trackpoint.
1087 Unfinished and experimental, needs gpsbabel, which is called from
1088 the program as "$Cmd{'gpsbabel'}".
1089 -o x, --output-format x
1090 Use output format x:
1091 clean
1093 gpsml (Default)
1094 gpstrans
1095 gpx (Not complete)
1096 poscount
1097 ps (Unfinished)
1098 svg (Unfinished)
1099 xgraph
1100 ygraph
1101 --outside
1102 Print only trackpoints outside a rectangle specified by --pos1 and
1103 --pos2.
1104 --pos1 x, --pos2 x
1105 Specifies one corner where x is in "lat,lon" format (decimal
1106 degrees, negative for west or south) of area rectangle used by the
1107 --inside and --outside options.
1108 -r x, --require x
1109 Specify requirements for trackpoints to be written. x is a string
1110 with the following flags:
1112 Print only waypoints which have an elevation.
1114 Print only waypoints which have a position.
1116 Print only waypoints which have a timestamp.
1117 -R x=y[,x2=y2[...]]
1118 Round trackpoint element x to y decimals. Example:
1119 --round lat=4,lon=5,ele=1
1120 -s, --short-date
1121 Use short date format.
1122 -S x, --save-to-file x
1123 Save the unconverted data to a file with a filename starting with
1124 the timestamp of the first trackpoint. The parameter string x is
1125 added at the end of the filename. For the time being this option
1126 will ignore all other options. Note: If several files are specified
1127 on the command line, all data will be saved into only one file. This
1128 behaviour may change in the future.
1129 -t, --create-breaks
1130 Create breaks in track between points with a difference more than
1131 $PAUSE_LIMIT seconds.
1132 -u, --comment-out-dups
1133 Comment out following data with identical position values, only
1134 print first entry.
1135 -v, --verbose
1136 Verbose output.
1137 --version
1138 Print version information.
1139 -w, --strip-whitespace
1140 Strip all unnecessary whitespace.
1141 -y, --double-y-scale
1142 Double Y scale (latitude) to get it right in gnuplot.
1143 --debug
1144 Print debugging messages.
1147 exit($Retval);
1148 # }}}
1149 } # usage()
1151 __END__
1153 # Plain Old Documentation (POD) {{{
1155 =pod
1157 =head1 NAME
1159 gptrans_conv
1161 =head1 REVISION
1163 $Id$
1165 =head1 SYNOPSIS
1167 B<gptrans_conv> [options] [file [files [...]]]
1169 B<gptrans_conv> -S [options] [file [files [...]]]
1171 B<gptrans_conv> -u [options] [file [files [...]]]
1173 =head1 DESCRIPTION
1175 Converts between various GPS formats.
1177 =head1 OPTIONS
1179 =over 4
1181 =item B<--chronology>
1183 Check for broken chronology, warn about entries with an old timestamp.
1185 =item B<-d>, B<--skip-dups>
1187 Skip duplicated coordinates.
1189 =item B<-e>, B<--epoch>
1191 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1193 item B<--fix>
1195 Comment out entries which is obviously wrong. Use together with
1196 --chronology to fix those kind of errors. Does not work with GPX output
1197 yet.
1199 =item B<-h>, B<--help>
1201 Show this help.
1203 =item B<--inside>
1205 Print only trackpoints inside a rectangle specified by --pos1 and
1206 --pos2.
1208 =item B<--near>
1210 Add names of the three closest waypoints to the trackpoint. Unfinished
1211 and experimental, needs gpsbabel.
1213 =item B<-n x>, B<--undefined x>
1215 Use x as undefined value.
1217 =item B<-o x>, B<--output-format x>
1219 Use output format x:
1221 =over 4
1223 =over 4
1225 =item clean
1227 =item csv
1229 =item gpsml (Default)
1231 This is the format which is meant to be used when storing the track
1232 logs.
1233 It is line-based XML which makes it easy to edit and grep. Probably not
1234 finished yet.
1236 =item gpstrans
1238 =item gpx (Not complete)
1240 =item poscount
1242 Creates a 3D plot where areas with many trackpoints are higher than
1243 areas with less track points.
1245 =item ps (Unfinished)
1247 =item svg (Unfinished)
1249 =item xgraph
1251 =item ygraph
1253 =back
1255 =back
1259 =item B<--outside>
1261 Print only trackpoints outside a rectangle specified by --pos1 and
1262 --pos2.
1264 =item B<--pos1 x>, B<--pos2 x>
1266 Specifies corners of an area rectangle used by the --inside and
1267 --outside options. The x value is in "lat,lon" format (decimal degrees,
1268 negative for west or south) .
1270 =item B<-r x>, B<--require x>
1272 Specify requirements for trackpoints to be written. x is a string with
1273 the following flags:
1275 =over 4
1277 =over 4
1279 =item e
1281 =over 4
1283 =item Print only waypoints which have an elevation.
1285 =back
1287 =back
1289 =over 4
1291 =item p
1293 =over 4
1295 =item Print only waypoints which have a position.
1297 =item t
1299 =over 4
1301 =item Print only waypoints which have a timestamp.
1303 =back
1305 =back
1307 =back
1311 =item -R x=y[,x2=y2[...]]
1313 Round trackpoint element x to y decimals.
1315 Example:
1317 --round lat=4,lon=5,ele=1
1319 =item B<-s>, B<--short-date>
1321 Use short date format.
1323 =item B<-S x>, B<--save-to-file x>
1325 Save the unconverted data to a file with a filename starting with the
1326 timestamp of the first trackpoint. The parameter string x is added at
1327 the end of the filename. For the time being this option will ignore all
1328 other options.
1330 Note: If several files are specified on the command line, all data will
1331 be saved into only one file. This behaviour may change in the future.
1333 =item B<-t>, B<--create-breaks>
1335 Create breaks in track between points with a difference more than the
1336 number of seconds specified by the C<$PAUSE_LIMIT> variable.
1338 =item B<-u>, B<--comment-out-dups>
1340 Comment out following data with identical position values, only print
1341 first entry.
1343 =item B<-v>, B<--verbose>
1345 Verbose output.
1347 =item B<-w>, B<--strip-whitespace>
1349 Strip all unnecessary whitespace.
1351 =item B<-y>, B<--double-y-scale>
1353 Double Y scale (latitude) to get it right in gnuplot.
1355 =item B<-h>, B<--help>
1357 Print a brief help summary.
1359 =item B<--version>
1361 Print version information.
1363 =item B<--debug>
1365 Print debugging messages.
1367 =back
1369 =head1 BUGS
1371 Pretty incomplete in some areas. Some of the source formats are
1372 undocumented and thus incomplete. Some functionality is not working
1373 properly, for example the Postscript output.
1375 =head1 AUTHOR
1377 Made by Øyvind A. Holm S<E<lt>sunny@sunbase.orgE<gt>>.
1379 =head1 COPYRIGHT
1381 Copyleft © Øyvind A. Holm &lt;sunny@sunbase.org&gt;
1382 This is free software; see the file F<COPYING> for legalese stuff.
1384 =head1 LICENCE
1386 This program is free software; you can redistribute it and/or modify it
1387 under the terms of the GNU General Public License as published by the
1388 Free Software Foundation; either version 2 of the License, or (at your
1389 option) any later version.
1391 This program is distributed in the hope that it will be useful, but
1392 WITHOUT ANY WARRANTY; without even the implied warranty of
1393 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1394 See the GNU General Public License for more details.
1396 You should have received a copy of the GNU General Public License along
1397 with this program; if not, write to the Free Software Foundation, Inc.,
1398 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1400 =head1 SEE ALSO
1402 gpsbabel(1)
1404 =cut
1406 # }}}
1408 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
1409 # End of file $Id$