3 #=======================================================================
5 # Converts between various GPS formats
8 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
9 # License: GNU General Public License version 2 or later, see end of
10 # file for legal stuff.
11 #=======================================================================
19 use Time
::Local qw
{ timegm_nocheck
};
22 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
37 # Initial values for command line arguments {{{
39 'comment-out-dups' => 0,
42 'double-y-scale' => 0,
49 'output-format' => "gpsml",
55 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
58 'strip-whitespace' => 0,
66 $progname =~ s/^.*\/(.*?)$/$1/;
69 my $id_date = $rcs_id;
70 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
72 push(@main::version_array
, $rcs_id);
74 Getopt
::Long
::Configure
("bundling");
76 # Command line options {{{
77 "chronology" => \
$Opt{'chronology'},
78 "comment-out-dups|u" => \
$Opt{'comment-out-dups'},
79 "create-breaks|t" => \
$Opt{'create-breaks'},
80 "debug" => \
$Opt{'debug'},
81 "double-y-scale|y" => \
$Opt{'double-y-scale'},
82 "epoch|e" => \
$Opt{'epoch'},
83 "fix" => \
$Opt{'fix'},
84 "from-date=s" => \
$Opt{'from-date'},
85 "help|h" => \
$Opt{'help'},
86 "inside" => \
$Opt{'inside'},
87 "near" => \
$Opt{'near'},
88 "output-format|o=s" => \
$Opt{'output-format'},
89 "outside" => \
$Opt{'outside'},
90 "pos1=s" => \
$Opt{'pos1'},
91 "pos2=s" => \
$Opt{'pos2'},
92 "require|r=s" => \
$Opt{'require'},
93 "round|R=s" => \
$Opt{'round'},
94 "save-to-file|S=s" => \
$Opt{'save-to-file'},
95 "short-date|s" => \
$Opt{'short-date'},
96 "skip-dups|d" => \
$Opt{'skip-dups'},
97 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
98 "undefined|n=s" => \
$Opt{'undefined'},
99 "verbose|v+" => \
$Opt{'verbose'},
100 "version" => \
$Opt{'version'},
102 ) || die("$progname: Option error. Use -h for help.\n");
106 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
108 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
109 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
110 my $Spc = $GPST::Spc
; # FIXME
111 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
114 my ($last_lon, $last_lat, $last_line) =
115 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
116 my ($lat1, $lon1, $lat2, $lon2) =
117 (-1000, -1000, 1000, 1000);
120 'gpsbabel' => '/usr/local/bin/gpsbabel',
125 if ($Opt{'output-format'} eq "pgtab") {
126 $Opt{'require'} .= "p";
129 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
130 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
131 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
133 $Opt{'require'} =~ /[^ept]/
134 && die("$0: Unknown flag in --require (-r) value\n");
136 $Opt{'debug'} && ($Debug = 1);
137 $Opt{'help'} && usage
(0);
138 $Opt{'version'} && print_version
();
140 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
144 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
159 if ($Opt{'epoch'} && $Opt{'short-date'}) {
160 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
163 if ($Opt{'inside'} && $Opt{'outside'}) {
164 die("$progname: Cannot mix the --inside and --outside options\n");
167 # To avoid printing out extra "/> at the start of svg output:
168 my $svg_start_thing = "";
172 if (defined($Opt{'round'})) {
173 my $R = $Opt{'round'};
174 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
177 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
178 # Kunne vært et eget script på grunn av at det gjør sine helt egne
179 # greier, men like greit å samle det på en plass.
180 # FIXME: Fjerner ikke første duplikatentryen.
181 # FIXME: Se om det går å få flytta den inn i print_entry() så man
182 # slipper å ha to gptrans_conv’er i pipen.
183 # FIXME: Legg inn alle formatene.
184 if ($Opt{'comment-out-dups'}) {
185 # Comment out areas without reception {{{
186 my ($start_date, $end_date, $found_dup) = ("", "", 0);
196 (\d\d
)/(\d\d)/(\d\d\d\d
)\x20 # Month/Day/Year — urgh
197 (\d\d
):(\d\d
):(\d\d
) # Hour:Min:Sec
201 my ($lat_val, $lon_val, $Speed, $Unkn,
202 $Month, $Day, $Year, $Hour, $Min, $Sec) =
204 $5, $6, $7, $8, $9, $10);
205 if (($lat_val eq $last_lat) && ($lon_val eq $last_lon)) {
206 unless ($found_dup) {
207 $start_date = "$Year$Month${Day}T$Hour$Min$Sec";
212 $end_date = "$Year$Month${Day}T$Hour$Min$Sec";
215 print("# $start_date-$end_date: " .
216 "CO: No signal \x7B\x7B\x7B\n");
220 print("# $start_date-$end_date: " .
221 "CO: No signal \x7D\x7D\x7D\n# move\n$_");
227 $last_lat = $lat_val;
228 $last_lon = $lon_val;
239 print("# $start_date-$end_date: " .
240 "CO: No signal \x7B\x7B\x7B\n");
244 print("# $start_date-$end_date: " .
245 "CO: No signal \x7D\x7D\x7D\n# move\n");
252 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
259 my $from_stdin = scalar(@ARGV) ?
0 : 1;
261 $from_stdin && push(@ARGV, "-");
263 for $curr_file (@ARGV) {
264 # Scan through stdin or specified files and send every GPS entry to
267 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
268 if (open(CurrFP
, "<$curr_file")) {
273 'year' => '', 'month' => '', 'day' => '',
274 'hour' => '', 'min' => '', 'sec' => '',
277 'lat' => '', 'lon' => '',
284 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
285 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
287 if ($Opt{'save-to-file'} ne "\n") {
288 push(@first_lines, $_);
290 s/^# error // && ($Dat{'error'} = "error");
291 s/^# ?// && ($Dat{'error'} = "desc");
293 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
294 # gpsml — The main storage format {{{
295 my ($Elem, $Props, $Data) =
297 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
298 $Elem eq "etp" && ($Dat{'error'} = $err_str);
300 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
302 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
304 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
305 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
310 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
311 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
312 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
313 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
316 } elsif (m
#^<break\b.*?/>#) {
318 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
322 } elsif (m
#^<desc\b.*?>(.*$)#s) {
323 $Dat{'type'} = "desc";
325 until ($Txt =~ m
#</desc>#s) {
328 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
333 $xml_data .= join("", <CurrFP
>);
334 if (!length($Opt{'output-format'})) {
335 $Opt{'output-format'} = "gpx";
336 print_header
(*STDOUT
);
338 read_xmlfile
($xml_data);
342 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
343 # CSV format, epoch style {{{
344 my ($ep_time, $lon_val, $lat_val, $Alt) =
346 $Dat{'epoch'} = $ep_time;
347 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
348 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
349 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
350 $Dat{'month'}++; # Urgh Ⅰ
351 $Dat{'year'} += 1900; # Urgh Ⅱ
356 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
357 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
360 # CSV format, human-readable date format {{{
361 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
362 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
363 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
369 } elsif (/^Trackpoint\t/) {
370 # 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 {{{
373 # N60.41630 E5.31675\t
374 # 09.02.2006 20:24:37 (UTC)\t
382 $Orig =~ s/[\r\n]+$//;
383 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
384 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
386 # Nødløsning for å unngå at variabler
388 "\t\t\t\t\t\t\t\t\t\t"
391 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
392 "Time_f=\"$Time_f\"\n",
393 "Alt_f=\"$Alt_f\"\n",
394 "Depth_f=\"$Depth_f\"\n",
395 "Leglength_f=\"$Leglength_f\"\n",
396 "Legtime_f=\"$Legtime_f\"\n",
397 "Legspeed_f=\"$Legspeed_f\"\n",
398 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
403 $Legtime_hour, $Legtime_min, $Legtime_sec,
404 $Legspeed, $Legspeed_unit,
406 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
407 "", "", "", "", "", "", "", "", "", "");
408 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
409 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
410 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
411 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
412 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
413 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
414 ($Dat{'ele'} = $1, $Alt_unit = $2);
415 D
("ele = \"$Dat{'ele'}\"");
416 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
417 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
418 # MapSource in win xp writes YYYY, but YY in win98se.
420 defined($Dat{'year'})
421 && $Dat{'year'} =~ /\d/
422 && $Dat{'year'} < 1900
423 ) && ($Dat{'year'} += 2000);
426 } elsif (/^Track\t(.*?)\t/) {
427 $Dat{'title'} = txt_to_xml
($1);
428 $Dat{'type'} = "title";
434 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
439 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
440 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
441 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
442 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
443 $lat_d, $lat_m, $lat_s,
444 $lon_d, $lon_m, $lon_s) =
449 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
450 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
451 $Dat{'lat
'} = sprintf("%${Flat}f",
452 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
453 $Dat{'lon
'} = sprintf("%${Flon}f",
454 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
459 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
460 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
463 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
464 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
466 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
467 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
475 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
485 (\d\d) # Latitude degree
486 (\d\d) # Latitude minute
487 (\d\d\d) # Latitude minute decimals
489 (\d\d\d) # Longitude degree
490 (\d\d) # Longitude minute
491 (\d\d\d) # Longitude minute degree
497 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
498 $lon_degmin, $lon_mindec);
499 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
500 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
501 $lat_degmin, $lat_mindec, $EW,
502 $lon_deg, $lon_degmin, $lon_mindec,
503 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
504 ($2+2000, $3, $4, $5,
509 my $ep_time = timegm_nocheck(
510 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
511 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
513 $last_time = $ep_time;
514 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
515 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
516 my $tmp_lon = sprintf(
521 my $tmp_lat = sprintf("%${Flat}f",
525 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
526 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
527 $Dat{'lat
'} = $tmp_lat;
528 $Dat{'lon
'} = $tmp_lon;
531 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
532 # @020721221336__________________________________________ {{{
533 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
534 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
537 $Dat{'error
'} = "nosignal";
541 # \t2003-12-02 10:06:17\tN50 05 31.2\tE14 26 10.4\t165.003417969
546 (\d\d\d\d)-(\d\d)-(\d\d)\ (\d\d):(\d\d):(\d\d)
548 (N|S)(\d+)\ (\d\d)\ (\d\d\.\d)
550 (W|E)(\d+)\ (\d\d)\ (\d\d\.\d)
555 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
556 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
557 ($1, $2, $3, $4, $5, $6);
559 $Dat{'lat
'} = dms_to_ddd($8, $9, $10);
561 $Dat{'lon
'} = dms_to_ddd($12, $13, $14);
563 $NS eq "S" && ($Dat{'lat
'} = 0 - $Dat{'lat
'});
564 $WE eq "W" && ($Dat{'lon
'} = 0 - $Dat{'lon
'});
570 } elsif (/^!T:\t(.*?)\t/) {
572 $Dat{'type
'} = "title";
574 } elsif (/^xmaplog /) {
577 ($Opt{'output
-format
'} eq "csv")
578 && ($Opt{'save
-to
-file
'} eq "\n")
580 } elsif (/^Pause: /) {
581 # NOP, is here to cope with old files I’ve lying around.
582 } elsif ($Dat{'error
'} eq "desc") {
584 if (defined($Comment)) {
585 $Comment =~ s/^\s*(.*?)\s*$/$1/;
586 if ($Opt{'output
-format
'} eq "gpsml") {
587 $Dat{'desc
'} = txt_to_xml($Comment);
588 $Dat{'type
'} = "desc";
593 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
598 warn("$progname: $curr_file: Cannot open file for read: $!\n");
603 print_footer(*STDOUT);
609 my $Txt = join("", @_);
610 $Txt =~ s/<!--(.*?)-->//gs;
611 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
619 # D("print_xml_gps(\"$Orig\")\n");
620 $Str =~ s/<!--(.*?)-->//gs;
621 my $fromdate_str = "";
622 if ($Opt{'from
-date
'}) {
623 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
625 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
629 <wpt\b(.*?)>(.*?)</wpt>
634 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
635 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
637 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
638 ($Lat = postgresql_copy_safe($1));
639 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
640 ($Lon = postgresql_copy_safe($1));
641 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
642 ($Name = postgresql_copy_safe(xml_to_txt($2)));
643 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
644 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
645 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
646 ($Type = postgresql_copy_safe(xml_to_txt($2)));
647 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
648 ($Time = postgresql_copy_safe(xml_to_txt($2)));
649 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
650 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
651 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
652 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
653 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
654 ($Src = postgresql_copy_safe(xml_to_txt($2)));
655 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
656 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
658 if ($Opt{'output
-format
'} eq "pgwtab") {
672 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
676 " UPDATE logg SET sted = clname(coor) " .
677 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
678 " UPDATE logg SET dist = cldist(coor) " .
679 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
690 <trk\b(.*?)>(.*?)</trk>
696 <name\b(.*?)>(.*?)</name>
699 $tmp_dat{'title
'} = $2;
700 $tmp_dat{'type
'} = "title";
701 $tmp_dat{'error
'} = "";
702 print_entry(%tmp_dat);
707 <trkseg\b(.*?)>(.*?)</trkseg>
713 <trkpt\b(.*?)>(.*?)</trkpt>
716 my ($attr_trkpt, $el_trkpt) =
719 'year
' => '', 'month
' => '', 'day
' => '',
720 'hour
' => '', 'min
' => '', 'sec
' => '',
723 'lat
' => '', 'lon
' => '',
729 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
730 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
731 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
735 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
736 (\d\d):?(\d\d):?([\d\.]+)Z</time>
739 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
740 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
741 ($1, $2, $3, $4, $5, $6);
757 local *OutFP = shift;
758 if ($Opt{'output
-format
'} eq "gpsml") {
760 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
764 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
765 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
766 "Datum[100]: WGS 84\n");
767 } elsif ($Opt{'output
-format
'} eq "gpx") {
769 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
771 qq{$Spc${Spc}version="1.1"\n},
772 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
773 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
774 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
775 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
776 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
779 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
781 } elsif ($Opt{'output
-format
'} eq "ps") {
782 print(OutFP ps_header(532, 6034, 533, 6040));
784 } elsif ($Opt{'output
-format
'} eq "svg") {
786 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
787 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
788 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
789 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
790 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
791 "$Spc$Spc<title></title>\n",
792 "$Spc$Spc<desc></desc>\n",
800 local *OutFP = shift;
801 if ($Opt{'output
-format
'} eq "gpsml") {
806 } elsif ($Opt{'output
-format
'} eq "gpx") {
808 "$Spc$Spc$Spc$Spc</trkseg>\n",
812 } elsif ($Opt{'output
-format
'} eq "poscount") {
813 while (my ($l_name, $l_val) = each %Poscount) {
814 $l_name =~ /^(.+?),(.+?)$/
815 && print(OutFP "$1\t$2\t$l_val\n");
817 } elsif ($Opt{'output
-format
'} eq "ps") {
823 } elsif ($Opt{'output
-format
'} eq "svg") {
824 print(OutFP "\"/>\n</svg>\n");
830 # Print a GPS entry with time, latitude, longitude and elevation in
834 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
835 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
836 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
837 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
838 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
839 my $print_time = length($Dat{'year
'}) ? 1 : 0;
840 my $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
842 $Dat{'lat
'} = $Dat{'lon
'} = "";
844 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
845 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
847 D("print_entry(\"" . join("\", \"", @_) . "\");");
850 if ($Opt{'near
'} && $print_pos) {
851 $Line .= sprintf("%s ",
852 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
855 if (length($Opt{'round
'})) {
856 for my $Tmp (qw{ lat lon ele }) {
857 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
859 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
864 if ($Opt{'output
-format
'} eq "poscount") {
865 if (!length($Dat{'error
'})) {
866 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
867 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
874 $ep_time = timegm_nocheck(
875 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
876 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
878 $Dat{'epoch
'} = $ep_time;
879 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
880 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
881 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
882 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
883 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
884 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
885 if ($Opt{'chronology
'}) {
886 if ($last_time > $ep_time && !length($Dat{'error
'})) {
888 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
889 $progname, sec_to_string($last_time, "T"),
890 sec_to_readable($last_time-$ep_time),
891 sec_to_string($ep_time, "T")
893 # FIXME: Make --fix work with gpx.
894 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
895 $Dat{'error
'} = "chrono";
897 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
899 "%s: $curr_file: \"%sZ\": Duplicated time\n",
900 $progname, sec_to_string($last_time, "T")
902 # FIXME: Make --fix work with gpx.
903 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
904 $Dat{'error
'} = "duptime";
918 if ($Opt{'save
-to
-file
'} ne "\n") {
920 $print_time || return;
921 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
922 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
923 "$Opt{'save
-to
-file
'}";
924 my $file_name = $base_name;
926 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
927 $file_name = "$base_name.dup_$a";
930 die("$progname: $base_name: File already exists, and ran " .
931 "out of attempts to create unique file name\n");
933 if ($Opt{'verbose
'}) {
934 warn("$progname: $base_name: File already exists, using " .
935 "unique name \"$file_name\" instead\n");
938 if (open(ToFP, ">", $file_name)) {
947 ) || die("$progname: $file_name: Cannot write to file: $!\n");
950 if ($Opt{'output
-format
'} eq "gpsml") {
951 printf("<include>%s</include>\n",
952 txt_to_xml($file_name));
953 } elsif ($Opt{'output
-format
'} eq "gpx") {
954 printf("<!-- Saved unconverted data to \"%s\" -->\n",
955 txt_to_xml($file_name));
957 print("$progname: Saved unconverted data to \"$file_name\"\n");
961 die("$progname: $file_name: Cannot create file: $!\n");
969 if ($Dat{'type
'} eq "tp") {
971 if ($Opt{'require'}) {
972 $Req{'time'} && !$print_time && return;
973 $Req{'position
'} && !$print_pos && return;
974 $Req{'ele
'} && !$print_ele && return;
977 if ($Opt{'inside
'} || $Opt{'outside
'}) {
979 ($Dat{'lat
'} < $lat1) ||
980 ($Dat{'lat
'} > $lat2) ||
981 ($Dat{'lon
'} < $lon1) ||
982 ($Dat{'lon
'} > $lon2)
984 $Opt{'inside
'} && return;
986 $Opt{'outside
'} && return;
990 if ($Opt{'output
-format
'} eq "ps") {
997 && ($Dat{'lon
'} eq $last_lon)
998 && ($Dat{'lat
'} eq $last_lat)
1000 if ($Opt{'output
-format
'} eq 'gpsml
') {
1001 $Dat{'error
'} = "dup";
1010 $Opt{'create
-breaks
'}
1011 && $ep_time-$last_time > $PAUSE_LIMIT
1014 $pause_len = $ep_time-$last_time;
1015 D("pause_len set to '$pause_len'");
1019 if ($Opt{'output
-format
'} eq "gpsml") {
1020 $Line .= sprintf("<pause>%s</pause>\n",
1021 sec_to_readable($ep_time-$last_time));
1022 } elsif ($Opt{'output
-format
'} eq "clean") {
1023 $pause_len && ($Line .= "\n");
1024 } elsif ($Opt{'output
-format
'} eq "csv") {
1025 $Line .= sprintf("# Pause: %s\n# move\n",
1026 sec_to_readable($ep_time-$last_time));
1027 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1028 $pause_len && ($Line .= "move ");
1035 # Valid data was found, send to stdout {{{
1036 unless ($first_time) {
1037 $first_time = $ep_time;
1039 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
1042 if ($Opt{'output
-format
'} eq "gpsml") {
1043 if ($Dat{'type
'} eq "tp") {
1044 $Dat{'format
'} = "gpsml";
1045 $Line .= trackpoint(%Dat);
1046 } elsif ($Dat{'type
'} =~ /^(pause|desc|title)$/) {
1047 $Line .= sprintf("<%s>%s</%s>\n",
1052 } elsif ($Opt{'output
-format
'} eq "pgtab") {
1053 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1054 $Dat{'format
'} = "pgtab";
1055 $Line .= trackpoint(%Dat);
1057 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1058 if ($print_pos && !length($Dat{'error
'})) {
1059 $Dat{'format
'} = "xgraph";
1060 $Line .= trackpoint(%Dat);
1062 } elsif($Opt{'output
-format
'} eq "gpstrans") {
1063 if ($print_pos && !length($Dat{'error
'})) {
1064 $Dat{'format
'} = "gpstrans";
1065 $Line .= trackpoint(%Dat);
1067 } elsif($Opt{'output
-format
'} eq "gpx") {
1068 $Dat{'format
'} = "gpx";
1069 if ($Dat{'type
'} =~ /^(tp|title)$/) {
1070 $Line .= trackpoint(%Dat);
1072 } elsif ($Opt{'output
-format
'} eq "clean") {
1073 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1074 $Dat{'format
'} = "clean";
1075 $Line .= trackpoint(%Dat);
1077 } elsif ($Opt{'output
-format
'} eq "ps") {
1080 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
1081 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
1083 } elsif ($Opt{'output
-format
'} eq "svg") {
1085 ($last_lon == 1000) || $pause_len
1087 "$svg_start_thing<path\n",
1088 " stroke=\"blue\"\n",
1089 " stroke-width=\"0.001\"\n",
1092 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1093 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1095 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1096 if (!length($Dat{'error
'})) {
1097 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1098 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1100 } elsif ($Opt{'output
-format
'} eq "csv") {
1102 if (!length($Dat{'error
'})) {
1103 $Dat{'format
'} = "csv";
1108 : $Opt{'short
-date
'}
1109 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1110 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1111 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1112 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1116 $print_ele ? $Dat{'ele
'} : "", # Elevation
1121 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1122 # FIXME: NOP at the moment.
1124 die("$progname: \"$Opt{'output
-format
'}\": " .
1125 "Unknown output format\n");
1130 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1131 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1136 if ($Opt{'output
-format
'} eq "gpsml") {
1137 $Line = "<break/>\n$Line";
1139 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1140 && ($Line .= "move $Line");
1141 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1142 if ($Opt{'output
-format
'} eq "gpx") {
1143 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1144 "$Spc$Spc$Spc$Spc<trkseg>\n";
1150 $print_time && ($last_time = $ep_time);
1152 $last_lon = $Dat{'lon
'};
1153 $last_lat = $Dat{'lat
'};
1155 $last_line = $data_line;
1156 $svg_start_thing = "\"/>\n";
1161 # Send a Postscript header to stdout {{{
1162 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1163 my $Date = sec_to_string(time);
1165 "%!PS-Adobe-3.0 EPSF-3.0\n",
1166 "%%Creator: $rcs_id\n",
1168 "%%CreationDate: $Date\n",
1169 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1170 "%%DocumentData: Clean7Bit\n",
1173 "/bd { bind def } bind def\n",
1174 "/incompound false def\n",
1175 "/m { moveto } bd\n",
1176 "/l { lineto } bd\n",
1177 "/c { curveto } bd\n",
1178 "/F { incompound not {fill} if } bd\n",
1179 "/f { closepath F } bd\n",
1180 "/S { stroke } bd\n",
1181 "/*u { /incompound true def } bd\n",
1182 "/*U { /incompound false def f} bd\n",
1183 "/k { setcmykcolor } bd\n",
1193 # Print program version {{{
1194 for (@main::version_array) {
1202 # Send the help message to stdout {{{
1209 Converts between various GPS formats.
1211 Usage: $progname [options] [file [files [...]]]
1212 $progname -S [file [files [...]]]
1213 $progname -u [file [files [...]]]
1218 Check for broken chronology, warn about entries with an old
1221 Skip duplicated coordinates.
1223 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1225 Comment out entries which is obviously wrong. Use together with
1226 --chronology to fix those kind of errors. Does not work with GPX
1229 Used by the pgwupd format. Specifies from which date waypoints
1230 should be updated. No checks for valid date format here, let
1231 PostgreSQL take care of that. All variants it understands can be
1236 Print only trackpoints inside a rectangle specified by --pos1 and
1239 Use x as undefined value. Default: "$Udef".
1241 Add names of the three closest waypoints to the trackpoint.
1242 Unfinished and experimental, needs gpsbabel, which is called from
1243 the program as "$Cmd{'gpsbabel
'}".
1244 -o, --output-format x
1245 Use output format x:
1260 Print only trackpoints outside a rectangle specified by --pos1 and
1264 Specifies one corner where x is in "lat,lon" format (decimal
1265 degrees, negative for west or south) of area rectangle used by the
1266 --inside and --outside options.
1268 Specify requirements for trackpoints to be written. x is a string
1269 with the following flags:
1271 Print only waypoints which have an elevation.
1273 Print only waypoints which have a position.
1275 Print only waypoints which have a timestamp.
1276 -R, --round x=y[,x2=y2[...]]
1277 Round trackpoint element x to y decimals. Example:
1278 --round lat=4,lon=5,ele=1
1280 Use short date format.
1281 -S, --save-to-file x
1282 Save the unconverted data to a file with a filename starting with
1283 the timestamp of the first trackpoint. The parameter string x is
1284 added at the end of the filename. For the time being this option
1285 will ignore all other options. Note: If several files are specified
1286 on the command line, all data will be saved into only one file. This
1287 behaviour may change in the future.
1289 Create breaks in track between points with a difference more than
1290 $PAUSE_LIMIT seconds.
1291 -u, --comment-out-dups
1292 Comment out following data with identical position values, only
1295 Increase level of verbosity. Can be repeated.
1297 Print version information.
1298 -w, --strip-whitespace
1299 Strip all unnecessary whitespace.
1300 -y, --double-y-scale
1301 Double Y scale (latitude) to get it right in gnuplot.
1303 Print debugging messages.
1311 # Print a status message to stderr based on verbosity level {{{
1312 my ($verbose_level, $Txt) = @_;
1314 if ($Opt{'verbose'} >= $verbose_level) {
1315 print(STDERR
"$progname: $Txt\n");
1323 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1325 # This program is free software; you can redistribute it and/or modify
1326 # it under the terms of the GNU General Public License as published by
1327 # the Free Software Foundation; either version 2 of the License, or (at
1328 # your option) any later version.
1330 # This program is distributed in the hope that it will be useful, but
1331 # WITHOUT ANY WARRANTY; without even the implied warranty of
1332 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1333 # See the GNU General Public License for more details.
1335 # You should have received a copy of the GNU General Public License
1336 # along with this program; if not, write to the Free Software
1337 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1341 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :