3 #=======================================================================
5 # Converts between various GPS formats
8 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
9 # License: GNU General Public License, see end of file for legal stuff.
10 #=======================================================================
14 use Time
::Local qw
{ timegm_nocheck
};
17 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
32 # Initial values for command line arguments {{{
34 'comment-out-dups' => 0,
37 'double-y-scale' => 0,
44 'output-format' => "gpsml",
50 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
54 'strip-whitespace' => 0,
61 $progname =~ s
#^.*/(.*?)$#$1#;
64 my $id_date = $rcs_id;
65 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
67 push(@main::version_array
, $rcs_id);
69 Getopt
::Long
::Configure
("bundling");
71 # Command line options {{{
72 "chronology" => \
$Opt{'chronology'},
73 "comment-out-dups|u" => \
$Opt{'comment-out-dups'},
74 "create-breaks|t" => \
$Opt{'create-breaks'},
75 "debug" => \
$Opt{'debug'},
76 "double-y-scale|y" => \
$Opt{'double-y-scale'},
77 "epoch|e" => \
$Opt{'epoch'},
78 "fix" => \
$Opt{'fix'},
79 "from-date=s" => \
$Opt{'from-date'},
80 "help|h" => \
$Opt{'help'},
81 "inside" => \
$Opt{'inside'},
82 "near" => \
$Opt{'near'},
83 "output-format|o=s" => \
$Opt{'output-format'},
84 "outside" => \
$Opt{'outside'},
85 "pos1=s" => \
$Opt{'pos1'},
86 "pos2=s" => \
$Opt{'pos2'},
87 "require|r=s" => \
$Opt{'require'},
88 "round|R=s" => \
$Opt{'round'},
89 "save-to-file|S=s" => \
$Opt{'save-to-file'},
90 "short-date|s" => \
$Opt{'short-date'},
91 "skip-dups|d" => \
$Opt{'skip-dups'},
92 "spread=i" => \
$Opt{'spread'}, # FIXME: Allow decimals some day
93 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
94 "undefined|n=s" => \
$Opt{'undefined'},
95 "verbose|v" => \
$Opt{'verbose'},
96 "version" => \
$Opt{'version'},
98 ) || die("$progname: Option error. Use -h for help.\n");
102 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
104 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
105 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
106 my $Spc = $GPST::Spc
; # FIXME
107 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
110 my ($last_lon, $last_lat, $last_line) =
111 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
112 my ($lat1, $lon1, $lat2, $lon2) =
113 (-1000, -1000, 1000, 1000);
116 'gpsbabel' => '/usr/local/bin/gpsbabel',
121 if ($Opt{'output-format'} eq "pgtab") {
122 $Opt{'require'} .= "p";
125 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
126 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
127 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
129 $Opt{'require'} =~ /[^ept]/
130 && die("$0: Unknown flag in --require (-r) value\n");
132 $Opt{'debug'} && ($Debug = 1);
133 $Opt{'help'} && usage
(0);
134 $Opt{'version'} && print_version
();
136 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
140 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
155 if ($Opt{'epoch'} && $Opt{'short-date'}) {
156 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
159 if ($Opt{'inside'} && $Opt{'outside'}) {
160 die("$progname: Cannot mix the --inside and --outside options\n");
163 # To avoid printing out extra "/> at the start of svg output:
164 my $svg_start_thing = "";
168 if (defined($Opt{'round'})) {
169 my $R = $Opt{'round'};
170 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
173 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
174 # Kunne vært et eget script på grunn av at det gjør sine helt egne
175 # greier, men like greit å samle det på en plass.
176 # FIXME: Fjerner ikke første duplikatentryen.
177 # FIXME: Se om det går å få flytta den inn i print_entry() så man
178 # slipper å ha to gptrans_conv’er i pipen.
179 # FIXME: Legg inn alle formatene.
180 if ($Opt{'comment-out-dups'}) {
181 # Comment out areas without reception {{{
182 my ($start_date, $end_date, $found_dup) = ("", "", 0);
192 (\d\d
)/(\d\d)/(\d\d\d\d
)\x20 # Month/Day/Year — urgh
193 (\d\d
):(\d\d
):(\d\d
) # Hour:Min:Sec
197 my ($lat_val, $lon_val, $Speed, $Unkn,
198 $Month, $Day, $Year, $Hour, $Min, $Sec) =
200 $5, $6, $7, $8, $9, $10);
201 if (($lat_val eq $last_lat) && ($lon_val eq $last_lon)) {
202 unless ($found_dup) {
203 $start_date = "$Year$Month${Day}T$Hour$Min$Sec";
208 $end_date = "$Year$Month${Day}T$Hour$Min$Sec";
211 print("# $start_date-$end_date: " .
212 "CO: No signal \x7B\x7B\x7B\n");
216 print("# $start_date-$end_date: " .
217 "CO: No signal \x7D\x7D\x7D\n# move\n$_");
223 $last_lat = $lat_val;
224 $last_lon = $lon_val;
235 print("# $start_date-$end_date: " .
236 "CO: No signal \x7B\x7B\x7B\n");
240 print("# $start_date-$end_date: " .
241 "CO: No signal \x7D\x7D\x7D\n# move\n");
248 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
255 my $from_stdin = scalar(@ARGV) ?
0 : 1;
257 $from_stdin && push(@ARGV, "-");
259 for $curr_file (@ARGV) {
260 # Scan through stdin or specified files and send every GPS entry to
263 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
264 if (open(CurrFP
, "<$curr_file")) {
269 'year' => '', 'month' => '', 'day' => '',
270 'hour' => '', 'min' => '', 'sec' => '',
273 'lat' => '', 'lon' => '',
280 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
281 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
283 if ($Opt{'save-to-file'} ne "\n") {
284 push(@first_lines, $_);
286 s/^# error // && ($Dat{'error'} = "error");
287 s/^# ?// && ($Dat{'error'} = "desc");
289 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
290 # gpsml — The main storage format {{{
291 my ($Elem, $Props, $Data) =
293 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
294 $Elem eq "etp" && ($Dat{'error'} = $err_str);
296 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
298 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
300 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
301 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
306 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
307 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
308 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
309 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
312 } elsif (m
#^<break\b.*?/>#) {
314 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
318 } elsif (m
#^<desc\b.*?>(.*$)#s) {
319 $Dat{'type'} = "desc";
321 until ($Txt =~ m
#</desc>#s) {
324 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
329 $xml_data .= join("", <CurrFP
>);
330 if (!length($Opt{'output-format'})) {
331 $Opt{'output-format'} = "gpx";
332 print_header
(*STDOUT
);
334 read_xmlfile
($xml_data);
338 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
339 # CSV format, epoch style {{{
340 my ($ep_time, $lon_val, $lat_val, $Alt) =
342 $Dat{'epoch'} = $ep_time;
343 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
344 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
345 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
346 $Dat{'month'}++; # Urgh Ⅰ
347 $Dat{'year'} += 1900; # Urgh Ⅱ
352 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
353 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
356 # CSV format, human-readable date format {{{
357 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
358 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
359 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
365 } elsif (/^Trackpoint\t/) {
366 # 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 {{{
369 # N60.41630 E5.31675\t
370 # 09.02.2006 20:24:37 (UTC)\t
378 $Orig =~ s/[\r\n]+$//;
379 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
380 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
382 # Nødløsning for å unngå at variabler
384 "\t\t\t\t\t\t\t\t\t\t"
387 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
388 "Time_f=\"$Time_f\"\n",
389 "Alt_f=\"$Alt_f\"\n",
390 "Depth_f=\"$Depth_f\"\n",
391 "Leglength_f=\"$Leglength_f\"\n",
392 "Legtime_f=\"$Legtime_f\"\n",
393 "Legspeed_f=\"$Legspeed_f\"\n",
394 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
399 $Legtime_hour, $Legtime_min, $Legtime_sec,
400 $Legspeed, $Legspeed_unit,
402 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
403 "", "", "", "", "", "", "", "", "", "");
404 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
405 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
406 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
407 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
408 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
409 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
410 ($Dat{'ele'} = $1, $Alt_unit = $2);
411 D
("ele = \"$Dat{'ele'}\"");
412 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
413 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
414 # MapSource in win xp writes YYYY, but YY in win98se.
416 defined($Dat{'year'})
417 && $Dat{'year'} =~ /\d/
418 && $Dat{'year'} < 1900
419 ) && ($Dat{'year'} += 2000);
422 } elsif (/^Track\t(.*?)\t/) {
423 $Dat{'title'} = txt_to_xml
($1);
424 $Dat{'type'} = "title";
430 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
435 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
436 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
437 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
438 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
439 $lat_d, $lat_m, $lat_s,
440 $lon_d, $lon_m, $lon_s) =
445 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
446 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
447 $Dat{'lat
'} = sprintf("%${Flat}f",
448 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
449 $Dat{'lon
'} = sprintf("%${Flon}f",
450 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
455 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
456 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
459 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
460 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
462 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
463 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
471 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
481 (\d\d) # Latitude degree
482 (\d\d) # Latitude minute
483 (\d\d\d) # Latitude minute decimals
485 (\d\d\d) # Longitude degree
486 (\d\d) # Longitude minute
487 (\d\d\d) # Longitude minute degree
493 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
494 $lon_degmin, $lon_mindec);
495 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
496 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
497 $lat_degmin, $lat_mindec, $EW,
498 $lon_deg, $lon_degmin, $lon_mindec,
499 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
500 ($2+2000, $3, $4, $5,
505 my $ep_time = timegm_nocheck(
506 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
507 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
509 $last_time = $ep_time;
510 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
511 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
512 my $tmp_lon = sprintf(
517 my $tmp_lat = sprintf("%${Flat}f",
521 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
522 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
523 $Dat{'lat
'} = $tmp_lat;
524 $Dat{'lon
'} = $tmp_lon;
527 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
528 # @020721221336__________________________________________ {{{
529 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
530 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
533 $Dat{'error
'} = "nosignal";
536 } elsif (/^xmaplog /) {
539 ($Opt{'output
-format
'} eq "csv")
540 && ($Opt{'save
-to
-file
'} eq "\n")
542 } elsif (/^Pause: /) {
543 # NOP, is here to cope with old files I’ve lying around.
544 } elsif ($Dat{'error
'} eq "desc") {
546 if (defined($Comment)) {
547 $Comment =~ s/^\s*(.*?)\s*$/$1/;
548 if ($Opt{'output
-format
'} eq "gpsml") {
549 $Dat{'desc
'} = txt_to_xml($Comment);
550 $Dat{'type
'} = "desc";
555 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
560 warn("$progname: $curr_file: Cannot open file for read: $!\n");
565 print_footer(*STDOUT);
571 my $Txt = join("", @_);
572 $Txt =~ s/<!--(.*?)-->//gs;
573 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
581 # D("print_xml_gps(\"$Orig\")\n");
582 $Str =~ s/<!--(.*?)-->//gs;
583 my $fromdate_str = "";
584 if ($Opt{'from
-date
'}) {
585 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
587 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
591 <wpt\b(.*?)>(.*?)</wpt>
596 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
597 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
599 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
600 ($Lat = postgresql_copy_safe($1));
601 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
602 ($Lon = postgresql_copy_safe($1));
603 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
604 ($Name = postgresql_copy_safe(xml_to_txt($2)));
605 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
606 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
607 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
608 ($Type = postgresql_copy_safe(xml_to_txt($2)));
609 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
610 ($Time = postgresql_copy_safe(xml_to_txt($2)));
611 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
612 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
613 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
614 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
615 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
616 ($Src = postgresql_copy_safe(xml_to_txt($2)));
617 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
618 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
620 if ($Opt{'output
-format
'} eq "pgwtab") {
634 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
638 " UPDATE logg SET sted = clname(coor) " .
639 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
640 " UPDATE logg SET dist = cldist(coor) " .
641 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
652 <trk\b(.*?)>(.*?)</trk>
658 <name\b(.*?)>(.*?)</name>
661 $tmp_dat{'title
'} = $2;
662 $tmp_dat{'type
'} = "title";
663 $tmp_dat{'error
'} = "";
664 print_entry(%tmp_dat);
669 <trkseg\b(.*?)>(.*?)</trkseg>
675 <trkpt\b(.*?)>(.*?)</trkpt>
678 my ($attr_trkpt, $el_trkpt) =
681 'year
' => '', 'month
' => '', 'day
' => '',
682 'hour
' => '', 'min
' => '', 'sec
' => '',
685 'lat
' => '', 'lon
' => '',
691 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
692 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
693 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
697 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
698 (\d\d):?(\d\d):?([\d\.]+)Z</time>
701 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
702 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
703 ($1, $2, $3, $4, $5, $6);
719 local *OutFP = shift;
720 if ($Opt{'output
-format
'} eq "gpsml") {
722 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
726 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
727 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
728 "Datum[100]: WGS 84\n");
729 } elsif ($Opt{'output
-format
'} eq "gpx") {
731 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
733 qq{$Spc${Spc}version="1.1"\n},
734 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
735 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
736 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
737 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
738 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
741 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
743 } elsif ($Opt{'output
-format
'} eq "ps") {
744 print(OutFP ps_header(532, 6034, 533, 6040));
746 } elsif ($Opt{'output
-format
'} eq "svg") {
748 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
749 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
750 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
751 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
752 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
753 "$Spc$Spc<title></title>\n",
754 "$Spc$Spc<desc></desc>\n",
762 local *OutFP = shift;
763 if ($Opt{'output
-format
'} eq "gpsml") {
768 } elsif ($Opt{'output
-format
'} eq "gpx") {
770 "$Spc$Spc$Spc$Spc</trkseg>\n",
774 } elsif ($Opt{'output
-format
'} eq "poscount") {
775 while (my ($l_name, $l_val) = each %Poscount) {
776 $l_name =~ /^(.+?),(.+?)$/
777 && print(OutFP "$1\t$2\t$l_val\n");
779 } elsif ($Opt{'output
-format
'} eq "ps") {
785 } elsif ($Opt{'output
-format
'} eq "svg") {
786 print(OutFP "\"/>\n</svg>\n");
792 # Print a GPS entry with time, latitude, longitude and elevation in
796 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
797 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
798 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
799 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
800 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
801 my $print_time = length($Dat{'year
'}) ? 1 : 0;
802 my $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
804 $Dat{'lat
'} = $Dat{'lon
'} = "";
806 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
807 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
809 D("print_entry(\"" . join("\", \"", @_) . "\");");
812 if ($Opt{'near
'} && $print_pos) {
813 $Line .= sprintf("%s ",
814 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
817 if (length($Opt{'round
'})) {
818 for my $Tmp (qw{ lat lon ele }) {
819 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
821 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
826 if ($Opt{'output
-format
'} eq "poscount") {
827 if (!length($Dat{'error
'})) {
828 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
829 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
836 D("\$print_time = '$print_time'");
837 $ep_time = timegm_nocheck(
838 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
839 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
841 $Dat{'epoch
'} = $ep_time;
842 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
843 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
844 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
845 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
846 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
847 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
849 if ($Opt{'spread
'}) {
850 D("Inside spread check, spread = '$Opt{'spread'}'");
852 D("Inside last_time, \$last_time = '$last_time', \$ep_time = '$ep_time'");
853 if ($ep_time - $last_time >= $Opt{'spread
'}) {
854 D("Inside '\
$last_time - \
$ep_time > 1' thingy");
856 my $bck_lasttime = $last_time;
857 my $bck_lastlat = $last_lat;
858 my $bck_lastlon = $last_lon;
859 my $bck_currtime = $ep_time;
860 my $bck_currlat = $Dat{'lat
'};
861 my $bck_currlon = $Dat{'lon
'};
862 my $bck_spread = $Opt{'spread
'};
864 for (my $tmpsec = $bck_lasttime;
865 $tmpsec < $bck_currtime;) {
866 D("\$tmpsec = '$tmpsec'");
867 print_entry(%tmp_dat);
868 if (defined($last_lat)) {
869 $tmp_dat{'lat
'} = current_pos($bck_lasttime, $bck_lastlat,
870 $bck_currtime, $bck_currlat,
872 $tmp_dat{'lon
'} = current_pos($bck_lasttime, $bck_lastlon,
873 $bck_currtime, $bck_currlon,
876 $tmpsec += $bck_spread;
877 $tmp_dat{'sec
'} += $bck_spread;
879 $Opt{'spread
'} = $bck_spread;
884 if ($Opt{'chronology
'}) {
885 if ($last_time > $ep_time && !length($Dat{'error
'})) {
887 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
888 $progname, sec_to_string($last_time, "T"),
889 sec_to_readable($last_time-$ep_time),
890 sec_to_string($ep_time, "T")
892 # FIXME: Make --fix work with gpx.
893 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
894 $Dat{'error
'} = "chrono";
896 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
898 "%s: $curr_file: \"%sZ\": Duplicated time\n",
899 $progname, sec_to_string($last_time, "T")
901 # FIXME: Make --fix work with gpx.
902 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
903 $Dat{'error
'} = "duptime";
917 if ($Opt{'save
-to
-file
'} ne "\n") {
919 $print_time || return;
920 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
921 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
922 "$Opt{'save
-to
-file
'}";
923 my $file_name = $base_name;
925 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
926 $file_name = "$base_name.dup_$a";
929 die("$progname: $base_name: File already exists, and ran " .
930 "out of attempts to create unique file name\n");
932 if ($Opt{'verbose
'}) {
933 warn("$progname: $base_name: File already exists, using " .
934 "unique name \"$file_name\" instead\n");
937 if (open(ToFP, ">", $file_name)) {
946 ) || die("$progname: $file_name: Cannot write to file: $!\n");
949 if ($Opt{'output
-format
'} eq "gpsml") {
950 printf("<include>%s</include>\n",
951 txt_to_xml($file_name));
952 } elsif ($Opt{'output
-format
'} eq "gpx") {
953 printf("<!-- Saved unconverted data to \"%s\" -->\n",
954 txt_to_xml($file_name));
956 print("$progname: Saved unconverted data to \"$file_name\"\n");
960 die("$progname: $file_name: Cannot create file: $!\n");
968 if ($Dat{'type
'} eq "tp") {
970 if ($Opt{'require'}) {
971 $Req{'time'} && !$print_time && return;
972 $Req{'position
'} && !$print_pos && return;
973 $Req{'ele
'} && !$print_ele && return;
976 if ($Opt{'inside
'} || $Opt{'outside
'}) {
978 ($Dat{'lat
'} < $lat1) ||
979 ($Dat{'lat
'} > $lat2) ||
980 ($Dat{'lon
'} < $lon1) ||
981 ($Dat{'lon
'} > $lon2)
983 $Opt{'inside
'} && return;
985 $Opt{'outside
'} && return;
989 if ($Opt{'output
-format
'} eq "ps") {
996 && ($Dat{'lon
'} eq $last_lon)
997 && ($Dat{'lat
'} eq $last_lat)
999 if ($Opt{'output
-format
'} eq 'gpsml
') {
1000 $Dat{'error
'} = "dup";
1009 $Opt{'create
-breaks
'}
1010 && $ep_time-$last_time > $PAUSE_LIMIT
1013 $pause_len = $ep_time-$last_time;
1014 D("pause_len set to '$pause_len'");
1018 if ($Opt{'output
-format
'} eq "gpsml") {
1019 $Line .= sprintf("<pause>%s</pause>\n",
1020 sec_to_readable($ep_time-$last_time));
1021 } elsif ($Opt{'output
-format
'} eq "clean") {
1022 $pause_len && ($Line .= "\n");
1023 } elsif ($Opt{'output
-format
'} eq "csv") {
1024 $Line .= sprintf("# Pause: %s\n# move\n",
1025 sec_to_readable($ep_time-$last_time));
1026 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1027 $pause_len && ($Line .= "move ");
1034 # Valid data was found, send to stdout {{{
1035 unless ($first_time) {
1036 $first_time = $ep_time;
1038 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
1041 if ($Opt{'output
-format
'} eq "gpsml") {
1042 if ($Dat{'type
'} eq "tp") {
1043 $Dat{'format
'} = "gpsml";
1044 $Line .= trackpoint(%Dat);
1045 } elsif ($Dat{'type
'} =~ /^(pause|desc|title)$/) {
1046 $Line .= sprintf("<%s>%s</%s>\n",
1051 } elsif ($Opt{'output
-format
'} eq "pgtab") {
1052 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1053 $Dat{'format
'} = "pgtab";
1054 $Line .= trackpoint(%Dat);
1056 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1057 if ($print_pos && !length($Dat{'error
'})) {
1058 $Dat{'format
'} = "xgraph";
1059 $Line .= trackpoint(%Dat);
1061 } elsif($Opt{'output
-format
'} eq "gpstrans") {
1062 if ($print_pos && !length($Dat{'error
'})) {
1063 $Dat{'format
'} = "gpstrans";
1064 $Line .= trackpoint(%Dat);
1066 } elsif($Opt{'output
-format
'} eq "gpx") {
1067 if ($Dat{'type
'} eq "tp") {
1068 $Dat{'format
'} = "gpx";
1069 $Line .= trackpoint(%Dat);
1071 } elsif ($Opt{'output
-format
'} eq "clean") {
1072 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
1073 $Dat{'format
'} = "clean";
1074 $Line .= trackpoint(%Dat);
1076 } elsif ($Opt{'output
-format
'} eq "ps") {
1079 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
1080 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
1082 } elsif ($Opt{'output
-format
'} eq "svg") {
1084 ($last_lon == 1000) || $pause_len
1086 "$svg_start_thing<path\n",
1087 " stroke=\"blue\"\n",
1088 " stroke-width=\"0.001\"\n",
1091 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1092 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1094 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1095 if (!length($Dat{'error
'})) {
1096 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1097 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1099 } elsif ($Opt{'output
-format
'} eq "csv") {
1101 if (!length($Dat{'error
'})) {
1102 $Dat{'format
'} = "csv";
1107 : $Opt{'short
-date
'}
1108 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1109 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1110 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1111 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1115 $print_ele ? $Dat{'ele
'} : "", # Elevation
1120 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1121 # FIXME: NOP at the moment.
1123 die("$progname: \"$Opt{'output
-format
'}\": " .
1124 "Unknown output format\n");
1129 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1130 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1135 if ($Opt{'output
-format
'} eq "gpsml") {
1136 $Line = "<break/>\n$Line";
1138 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1139 && ($Line .= "move $Line");
1140 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1141 if ($Opt{'output
-format
'} eq "gpx") {
1142 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1143 "$Spc$Spc$Spc$Spc<trkseg>\n";
1147 $Opt{'spread
'} || print($Line);
1149 $print_time && ($last_time = $ep_time);
1151 $last_lon = $Dat{'lon
'};
1152 $last_lat = $Dat{'lat
'};
1154 $last_line = $data_line;
1155 $svg_start_thing = "\"/>\n";
1160 # Send a Postscript header to stdout {{{
1161 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1162 my $Date = sec_to_string(time);
1164 "%!PS-Adobe-3.0 EPSF-3.0\n",
1165 "%%Creator: $rcs_id\n",
1167 "%%CreationDate: $Date\n",
1168 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1169 "%%DocumentData: Clean7Bit\n",
1172 "/bd { bind def } bind def\n",
1173 "/incompound false def\n",
1174 "/m { moveto } bd\n",
1175 "/l { lineto } bd\n",
1176 "/c { curveto } bd\n",
1177 "/F { incompound not {fill} if } bd\n",
1178 "/f { closepath F } bd\n",
1179 "/S { stroke } bd\n",
1180 "/*u { /incompound true def } bd\n",
1181 "/*U { /incompound false def f} bd\n",
1182 "/k { setcmykcolor } bd\n",
1192 # Print program version {{{
1193 for (@main::version_array) {
1201 # Send the help message to stdout {{{
1208 Converts between various GPS formats.
1210 Usage: $progname [options] [file [files [...]]]
1211 $progname -S [file [files [...]]]
1212 $progname -u [file [files [...]]]
1217 Check for broken chronology, warn about entries with an old
1220 Skip duplicated coordinates.
1222 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1224 Comment out entries which is obviously wrong. Use together with
1225 --chronology to fix those kind of errors. Does not work with GPX
1228 Used by the pgwupd format. Specifies from which date waypoints
1229 should be updated. No checks for valid date format here, let
1230 PostgreSQL take care of that. All variants it understands can be
1235 Print only trackpoints inside a rectangle specified by --pos1 and
1238 Use x as undefined value. Default: "$Udef".
1240 Add names of the three closest waypoints to the trackpoint.
1241 Unfinished and experimental, needs gpsbabel, which is called from
1242 the program as "$Cmd{'gpsbabel
'}".
1243 -o, --output-format x
1244 Use output format x:
1259 Print only trackpoints outside a rectangle specified by --pos1 and
1263 Specifies one corner where x is in "lat,lon" format (decimal
1264 degrees, negative for west or south) of area rectangle used by the
1265 --inside and --outside options.
1267 Specify requirements for trackpoints to be written. x is a string
1268 with the following flags:
1270 Print only waypoints which have an elevation.
1272 Print only waypoints which have a position.
1274 Print only waypoints which have a timestamp.
1275 -R, --round x=y[,x2=y2[...]]
1276 Round trackpoint element x to y decimals. Example:
1277 --round lat=4,lon=5,ele=1
1279 Use short date format.
1280 -S, --save-to-file x
1281 Save the unconverted data to a file with a filename starting with
1282 the timestamp of the first trackpoint. The parameter string x is
1283 added at the end of the filename. For the time being this option
1284 will ignore all other options. Note: If several files are specified
1285 on the command line, all data will be saved into only one file. This
1286 behaviour may change in the future.
1288 Generate "missing" points by calculating estimated position every x
1291 Create breaks in track between points with a difference more than
1292 $PAUSE_LIMIT seconds.
1293 -u, --comment-out-dups
1294 Comment out following data with identical position values, only
1299 Print version information.
1300 -w, --strip-whitespace
1301 Strip all unnecessary whitespace.
1302 -y, --double-y-scale
1303 Double Y scale (latitude) to get it right in gnuplot.
1305 Print debugging messages.
1315 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1317 # This program is free software; you can redistribute it and/or modify
1318 # it under the terms of the GNU General Public License as published by
1319 # the Free Software Foundation; either version 2 of the License, or (at
1320 # your option) any later version.
1322 # This program is distributed in the hope that it will be useful, but
1323 # WITHOUT ANY WARRANTY; without even the implied warranty of
1324 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1325 # See the GNU General Public License for more details.
1327 # You should have received a copy of the GNU General Public License
1328 # along with this program; if not, write to the Free Software
1329 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1333 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :