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";
540 } elsif (/^xmaplog /) {
543 ($Opt{'output
-format
'} eq "csv")
544 && ($Opt{'save
-to
-file
'} eq "\n")
546 } elsif (/^Pause: /) {
547 # NOP, is here to cope with old files I’ve lying around.
548 } elsif ($Dat{'error
'} eq "desc") {
550 if (defined($Comment)) {
551 $Comment =~ s/^\s*(.*?)\s*$/$1/;
552 if ($Opt{'output
-format
'} eq "gpsml") {
553 $Dat{'desc
'} = txt_to_xml($Comment);
554 $Dat{'type
'} = "desc";
559 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
564 warn("$progname: $curr_file: Cannot open file for read: $!\n");
569 print_footer(*STDOUT);
575 my $Txt = join("", @_);
576 $Txt =~ s/<!--(.*?)-->//gs;
577 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
585 # D("print_xml_gps(\"$Orig\")\n");
586 $Str =~ s/<!--(.*?)-->//gs;
587 my $fromdate_str = "";
588 if ($Opt{'from
-date
'}) {
589 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
591 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
595 <wpt\b(.*?)>(.*?)</wpt>
600 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
601 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
603 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
604 ($Lat = postgresql_copy_safe($1));
605 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
606 ($Lon = postgresql_copy_safe($1));
607 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
608 ($Name = postgresql_copy_safe(xml_to_txt($2)));
609 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
610 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
611 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
612 ($Type = postgresql_copy_safe(xml_to_txt($2)));
613 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
614 ($Time = postgresql_copy_safe(xml_to_txt($2)));
615 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
616 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
617 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
618 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
619 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
620 ($Src = postgresql_copy_safe(xml_to_txt($2)));
621 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
622 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
624 if ($Opt{'output
-format
'} eq "pgwtab") {
638 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
642 " UPDATE logg SET sted = clname(coor) " .
643 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
644 " UPDATE logg SET dist = cldist(coor) " .
645 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
656 <trk\b(.*?)>(.*?)</trk>
662 <name\b(.*?)>(.*?)</name>
665 $tmp_dat{'title
'} = $2;
666 $tmp_dat{'type
'} = "title";
667 $tmp_dat{'error
'} = "";
668 print_entry(%tmp_dat);
673 <trkseg\b(.*?)>(.*?)</trkseg>
679 <trkpt\b(.*?)>(.*?)</trkpt>
682 my ($attr_trkpt, $el_trkpt) =
685 'year
' => '', 'month
' => '', 'day
' => '',
686 'hour
' => '', 'min
' => '', 'sec
' => '',
689 'lat
' => '', 'lon
' => '',
695 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
696 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
697 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
701 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
702 (\d\d):?(\d\d):?([\d\.]+)Z</time>
705 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
706 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
707 ($1, $2, $3, $4, $5, $6);
723 local *OutFP = shift;
724 if ($Opt{'output
-format
'} eq "gpsml") {
726 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
730 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
731 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
732 "Datum[100]: WGS 84\n");
733 } elsif ($Opt{'output
-format
'} eq "gpx") {
735 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
737 qq{$Spc${Spc}version="1.1"\n},
738 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
739 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
740 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
741 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
742 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
745 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
747 } elsif ($Opt{'output
-format
'} eq "ps") {
748 print(OutFP ps_header(532, 6034, 533, 6040));
750 } elsif ($Opt{'output
-format
'} eq "svg") {
752 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
753 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
754 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
755 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
756 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
757 "$Spc$Spc<title></title>\n",
758 "$Spc$Spc<desc></desc>\n",
766 local *OutFP = shift;
767 if ($Opt{'output
-format
'} eq "gpsml") {
772 } elsif ($Opt{'output
-format
'} eq "gpx") {
774 "$Spc$Spc$Spc$Spc</trkseg>\n",
778 } elsif ($Opt{'output
-format
'} eq "poscount") {
779 while (my ($l_name, $l_val) = each %Poscount) {
780 $l_name =~ /^(.+?),(.+?)$/
781 && print(OutFP "$1\t$2\t$l_val\n");
783 } elsif ($Opt{'output
-format
'} eq "ps") {
789 } elsif ($Opt{'output
-format
'} eq "svg") {
790 print(OutFP "\"/>\n</svg>\n");
796 # Print a GPS entry with time, latitude, longitude and elevation in
800 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
801 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
802 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
803 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
804 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
805 my $print_time = length($Dat{'year
'}) ? 1 : 0;
806 my $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
808 $Dat{'lat
'} = $Dat{'lon
'} = "";
810 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
811 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
813 D("print_entry(\"" . join("\", \"", @_) . "\");");
816 if ($Opt{'near
'} && $print_pos) {
817 $Line .= sprintf("%s ",
818 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
821 if (length($Opt{'round
'})) {
822 for my $Tmp (qw{ lat lon ele }) {
823 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
825 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
830 if ($Opt{'output
-format
'} eq "poscount") {
831 if (!length($Dat{'error
'})) {
832 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
833 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
840 $ep_time = timegm_nocheck(
841 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
842 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
844 $Dat{'epoch
'} = $ep_time;
845 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
846 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
847 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
848 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
849 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
850 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
851 if ($Opt{'chronology
'}) {
852 if ($last_time > $ep_time && !length($Dat{'error
'})) {
854 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
855 $progname, sec_to_string($last_time, "T"),
856 sec_to_readable($last_time-$ep_time),
857 sec_to_string($ep_time, "T")
859 # FIXME: Make --fix work with gpx.
860 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
861 $Dat{'error
'} = "chrono";
863 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
865 "%s: $curr_file: \"%sZ\": Duplicated time\n",
866 $progname, sec_to_string($last_time, "T")
868 # FIXME: Make --fix work with gpx.
869 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
870 $Dat{'error
'} = "duptime";
884 if ($Opt{'save
-to
-file
'} ne "\n") {
886 $print_time || return;
887 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
888 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
889 "$Opt{'save
-to
-file
'}";
890 my $file_name = $base_name;
892 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
893 $file_name = "$base_name.dup_$a";
896 die("$progname: $base_name: File already exists, and ran " .
897 "out of attempts to create unique file name\n");
899 if ($Opt{'verbose
'}) {
900 warn("$progname: $base_name: File already exists, using " .
901 "unique name \"$file_name\" instead\n");
904 if (open(ToFP, ">", $file_name)) {
913 ) || die("$progname: $file_name: Cannot write to file: $!\n");
916 if ($Opt{'output
-format
'} eq "gpsml") {
917 printf("<include>%s</include>\n",
918 txt_to_xml($file_name));
919 } elsif ($Opt{'output
-format
'} eq "gpx") {
920 printf("<!-- Saved unconverted data to \"%s\" -->\n",
921 txt_to_xml($file_name));
923 print("$progname: Saved unconverted data to \"$file_name\"\n");
927 die("$progname: $file_name: Cannot create file: $!\n");
935 if ($Dat{'type
'} eq "tp") {
937 if ($Opt{'require'}) {
938 $Req{'time'} && !$print_time && return;
939 $Req{'position
'} && !$print_pos && return;
940 $Req{'ele
'} && !$print_ele && return;
943 if ($Opt{'inside
'} || $Opt{'outside
'}) {
945 ($Dat{'lat
'} < $lat1) ||
946 ($Dat{'lat
'} > $lat2) ||
947 ($Dat{'lon
'} < $lon1) ||
948 ($Dat{'lon
'} > $lon2)
950 $Opt{'inside
'} && return;
952 $Opt{'outside
'} && return;
956 if ($Opt{'output
-format
'} eq "ps") {
963 && ($Dat{'lon
'} eq $last_lon)
964 && ($Dat{'lat
'} eq $last_lat)
966 if ($Opt{'output
-format
'} eq 'gpsml
') {
967 $Dat{'error
'} = "dup";
976 $Opt{'create
-breaks
'}
977 && $ep_time-$last_time > $PAUSE_LIMIT
980 $pause_len = $ep_time-$last_time;
981 D("pause_len set to '$pause_len'");
985 if ($Opt{'output
-format
'} eq "gpsml") {
986 $Line .= sprintf("<pause>%s</pause>\n",
987 sec_to_readable($ep_time-$last_time));
988 } elsif ($Opt{'output
-format
'} eq "clean") {
989 $pause_len && ($Line .= "\n");
990 } elsif ($Opt{'output
-format
'} eq "csv") {
991 $Line .= sprintf("# Pause: %s\n# move\n",
992 sec_to_readable($ep_time-$last_time));
993 } elsif ($Opt{'output
-format
'} eq "xgraph") {
994 $pause_len && ($Line .= "move ");
1001 # Valid data was found, send to stdout {{{
1002 unless ($first_time) {
1003 $first_time = $ep_time;
1005 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
1008 if ($Opt{'output
-format
'} eq "gpsml") {
1009 if ($Dat{'type
'} eq "tp") {
1010 $Dat{'format
'} = "gpsml";
1011 $Line .= trackpoint(%Dat);
1012 } elsif ($Dat{'type
'} =~ /^(pause|desc|title)$/) {
1013 $Line .= sprintf("<%s>%s</%s>\n",
1018 } elsif ($Opt{'output
-format
'} eq "pgtab") {
1019 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1020 $Dat{'format
'} = "pgtab";
1021 $Line .= trackpoint(%Dat);
1023 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1024 if ($print_pos && !length($Dat{'error
'})) {
1025 $Dat{'format
'} = "xgraph";
1026 $Line .= trackpoint(%Dat);
1028 } elsif($Opt{'output
-format
'} eq "gpstrans") {
1029 if ($print_pos && !length($Dat{'error
'})) {
1030 $Dat{'format
'} = "gpstrans";
1031 $Line .= trackpoint(%Dat);
1033 } elsif($Opt{'output
-format
'} eq "gpx") {
1034 if ($Dat{'type
'} eq "tp") {
1035 $Dat{'format
'} = "gpx";
1036 $Line .= trackpoint(%Dat);
1038 } elsif ($Opt{'output
-format
'} eq "clean") {
1039 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1040 $Dat{'format
'} = "clean";
1041 $Line .= trackpoint(%Dat);
1043 } elsif ($Opt{'output
-format
'} eq "ps") {
1046 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
1047 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
1049 } elsif ($Opt{'output
-format
'} eq "svg") {
1051 ($last_lon == 1000) || $pause_len
1053 "$svg_start_thing<path\n",
1054 " stroke=\"blue\"\n",
1055 " stroke-width=\"0.001\"\n",
1058 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1059 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1061 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1062 if (!length($Dat{'error
'})) {
1063 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1064 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1066 } elsif ($Opt{'output
-format
'} eq "csv") {
1068 if (!length($Dat{'error
'})) {
1069 $Dat{'format
'} = "csv";
1074 : $Opt{'short
-date
'}
1075 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1076 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1077 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1078 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1082 $print_ele ? $Dat{'ele
'} : "", # Elevation
1087 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1088 # FIXME: NOP at the moment.
1090 die("$progname: \"$Opt{'output
-format
'}\": " .
1091 "Unknown output format\n");
1096 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1097 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1102 if ($Opt{'output
-format
'} eq "gpsml") {
1103 $Line = "<break/>\n$Line";
1105 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1106 && ($Line .= "move $Line");
1107 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1108 if ($Opt{'output
-format
'} eq "gpx") {
1109 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1110 "$Spc$Spc$Spc$Spc<trkseg>\n";
1116 $print_time && ($last_time = $ep_time);
1118 $last_lon = $Dat{'lon
'};
1119 $last_lat = $Dat{'lat
'};
1121 $last_line = $data_line;
1122 $svg_start_thing = "\"/>\n";
1127 # Send a Postscript header to stdout {{{
1128 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1129 my $Date = sec_to_string(time);
1131 "%!PS-Adobe-3.0 EPSF-3.0\n",
1132 "%%Creator: $rcs_id\n",
1134 "%%CreationDate: $Date\n",
1135 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1136 "%%DocumentData: Clean7Bit\n",
1139 "/bd { bind def } bind def\n",
1140 "/incompound false def\n",
1141 "/m { moveto } bd\n",
1142 "/l { lineto } bd\n",
1143 "/c { curveto } bd\n",
1144 "/F { incompound not {fill} if } bd\n",
1145 "/f { closepath F } bd\n",
1146 "/S { stroke } bd\n",
1147 "/*u { /incompound true def } bd\n",
1148 "/*U { /incompound false def f} bd\n",
1149 "/k { setcmykcolor } bd\n",
1159 # Print program version {{{
1160 for (@main::version_array) {
1168 # Send the help message to stdout {{{
1175 Converts between various GPS formats.
1177 Usage: $progname [options] [file [files [...]]]
1178 $progname -S [file [files [...]]]
1179 $progname -u [file [files [...]]]
1184 Check for broken chronology, warn about entries with an old
1187 Skip duplicated coordinates.
1189 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1191 Comment out entries which is obviously wrong. Use together with
1192 --chronology to fix those kind of errors. Does not work with GPX
1195 Used by the pgwupd format. Specifies from which date waypoints
1196 should be updated. No checks for valid date format here, let
1197 PostgreSQL take care of that. All variants it understands can be
1202 Print only trackpoints inside a rectangle specified by --pos1 and
1205 Use x as undefined value. Default: "$Udef".
1207 Add names of the three closest waypoints to the trackpoint.
1208 Unfinished and experimental, needs gpsbabel, which is called from
1209 the program as "$Cmd{'gpsbabel
'}".
1210 -o, --output-format x
1211 Use output format x:
1226 Print only trackpoints outside a rectangle specified by --pos1 and
1230 Specifies one corner where x is in "lat,lon" format (decimal
1231 degrees, negative for west or south) of area rectangle used by the
1232 --inside and --outside options.
1234 Specify requirements for trackpoints to be written. x is a string
1235 with the following flags:
1237 Print only waypoints which have an elevation.
1239 Print only waypoints which have a position.
1241 Print only waypoints which have a timestamp.
1242 -R, --round x=y[,x2=y2[...]]
1243 Round trackpoint element x to y decimals. Example:
1244 --round lat=4,lon=5,ele=1
1246 Use short date format.
1247 -S, --save-to-file x
1248 Save the unconverted data to a file with a filename starting with
1249 the timestamp of the first trackpoint. The parameter string x is
1250 added at the end of the filename. For the time being this option
1251 will ignore all other options. Note: If several files are specified
1252 on the command line, all data will be saved into only one file. This
1253 behaviour may change in the future.
1255 Create breaks in track between points with a difference more than
1256 $PAUSE_LIMIT seconds.
1257 -u, --comment-out-dups
1258 Comment out following data with identical position values, only
1261 Increase level of verbosity. Can be repeated.
1263 Print version information.
1264 -w, --strip-whitespace
1265 Strip all unnecessary whitespace.
1266 -y, --double-y-scale
1267 Double Y scale (latitude) to get it right in gnuplot.
1269 Print debugging messages.
1277 # Print a status message to stderr based on verbosity level {{{
1278 my ($verbose_level, $Txt) = @_;
1280 if ($Opt{'verbose'} >= $verbose_level) {
1281 print(STDERR
"$progname: $Txt\n");
1289 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1291 # This program is free software; you can redistribute it and/or modify
1292 # it under the terms of the GNU General Public License as published by
1293 # the Free Software Foundation; either version 2 of the License, or (at
1294 # your option) any later version.
1296 # This program is distributed in the hope that it will be useful, but
1297 # WITHOUT ANY WARRANTY; without even the implied warranty of
1298 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1299 # See the GNU General Public License for more details.
1301 # You should have received a copy of the GNU General Public License
1302 # along with this program; if not, write to the Free Software
1303 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1307 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :