3 #=======================================================================
5 # File ID: 082106ec-f924-11dd-b757-0001805bf4b1
6 # Converts between various GPS formats
9 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
10 # License: GNU General Public License version 2 or later, see end of
11 # file for legal stuff.
12 #=======================================================================
20 use Time
::Local qw
{ timegm_nocheck
};
23 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
38 # Initial values for command line arguments {{{
43 'double-y-scale' => 0,
50 'output-format' => "gpsml",
56 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
59 'strip-whitespace' => 0,
69 $progname =~ s/^.*\/(.*?)$/$1/;
72 my $id_date = $rcs_id;
73 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
75 push(@main::version_array
, $rcs_id);
77 Getopt
::Long
::Configure
("bundling");
79 # Command line options {{{
81 "chronology" => \
$Opt{'chronology'},
82 "create-breaks|t" => \
$Opt{'create-breaks'},
83 "debug" => \
$Opt{'debug'},
84 "double-y-scale|y" => \
$Opt{'double-y-scale'},
85 "epoch|e" => \
$Opt{'epoch'},
86 "fix" => \
$Opt{'fix'},
87 "from-date=s" => \
$Opt{'from-date'},
88 "help|h" => \
$Opt{'help'},
89 "inside" => \
$Opt{'inside'},
90 "near" => \
$Opt{'near'},
91 "output-format|o=s" => \
$Opt{'output-format'},
92 "outside" => \
$Opt{'outside'},
93 "pos1=s" => \
$Opt{'pos1'},
94 "pos2=s" => \
$Opt{'pos2'},
95 "require|r=s" => \
$Opt{'require'},
96 "round|R=s" => \
$Opt{'round'},
97 "save-to-file|S=s" => \
$Opt{'save-to-file'},
98 "short-date|s" => \
$Opt{'short-date'},
99 "skip-dups|d" => \
$Opt{'skip-dups'},
100 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
101 "time-shift|T=i" => \
$Opt{'time-shift'},
102 "undefined|n=s" => \
$Opt{'undefined'},
103 "verbose|v+" => \
$Opt{'verbose'},
104 "version" => \
$Opt{'version'},
107 ) || die("$progname: Option error. Use -h for help.\n");
111 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
113 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
114 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
115 my $Spc = $GPST::Spc
; # FIXME
116 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
119 my ($last_lon, $last_lat, $last_line) =
120 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
121 my ($lat1, $lon1, $lat2, $lon2) =
122 (-1000, -1000, 1000, 1000);
125 'gpsbabel' => '/usr/local/bin/gpsbabel',
130 if ($Opt{'output-format'} eq "pgtab") {
131 $Opt{'require'} .= "p";
134 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
135 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
136 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
138 $Opt{'require'} =~ /[^ept]/
139 && die("$0: Unknown flag in --require (-r) value\n");
141 $Opt{'debug'} && ($Debug = 1);
142 $Opt{'help'} && usage
(0);
143 if ($Opt{'version'}) {
148 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
152 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
167 if ($Opt{'epoch'} && $Opt{'short-date'}) {
168 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
171 if ($Opt{'inside'} && $Opt{'outside'}) {
172 die("$progname: Cannot mix the --inside and --outside options\n");
175 # To avoid printing out extra "/> at the start of svg output:
176 my $svg_start_thing = "";
180 if (defined($Opt{'round'})) {
181 my $R = $Opt{'round'};
182 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
185 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
187 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
194 my $from_stdin = scalar(@ARGV) ?
0 : 1;
196 $from_stdin && push(@ARGV, "-");
198 for $curr_file (@ARGV) {
199 # Scan through stdin or specified files and send every GPS entry to
202 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
203 if (open(CurrFP
, "<$curr_file")) {
208 'year' => '', 'month' => '', 'day' => '',
209 'hour' => '', 'min' => '', 'sec' => '',
212 'lat' => '', 'lon' => '',
219 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
220 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
222 if ($Opt{'save-to-file'} ne "\n") {
223 push(@first_lines, $_);
225 s/^# error // && ($Dat{'error'} = "error");
226 s/^# ?// && ($Dat{'error'} = "desc");
228 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
229 # gpsml — The main storage format {{{
230 my ($Elem, $Props, $Data) =
232 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
233 $Elem eq "etp" && ($Dat{'error'} = $err_str);
235 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
237 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
239 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
240 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
245 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
246 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
247 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
248 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
251 } elsif (m
#^<break\b.*?/>#) {
253 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
257 } elsif (m
#^<desc\b.*?>(.*$)#s) {
258 $Dat{'what'} = "desc";
260 until ($Txt =~ m
#</desc>#s) {
263 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
268 $xml_data .= join("", <CurrFP
>);
269 if (!length($Opt{'output-format'})) {
270 $Opt{'output-format'} = "gpx";
271 print_header
(*STDOUT
);
273 read_xmlfile
($xml_data);
277 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
278 # CSV format, epoch style {{{
279 my ($ep_time, $lon_val, $lat_val, $Alt) =
281 $Dat{'epoch'} = $ep_time;
282 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
283 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
284 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
285 $Dat{'month'}++; # Urgh Ⅰ
286 $Dat{'year'} += 1900; # Urgh Ⅱ
291 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
292 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
295 # CSV format, human-readable date format {{{
296 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
297 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
298 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
304 } elsif (/^Trackpoint\t/) {
305 # 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 {{{
308 # N60.41630 E5.31675\t
309 # 09.02.2006 20:24:37 (UTC)\t
317 $Orig =~ s/[\r\n]+$//;
318 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
319 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
321 # Nødløsning for å unngå at variabler
323 "\t\t\t\t\t\t\t\t\t\t"
326 # "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
327 # "Time_f=\"$Time_f\"\n",
328 # "Alt_f=\"$Alt_f\"\n",
329 # "Depth_f=\"$Depth_f\"\n",
330 # "Leglength_f=\"$Leglength_f\"\n",
331 # "Legtime_f=\"$Legtime_f\"\n",
332 # "Legspeed_f=\"$Legspeed_f\"\n",
333 # "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
338 $Legtime_hour, $Legtime_min, $Legtime_sec,
339 $Legspeed, $Legspeed_unit,
341 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
342 "", "", "", "", "", "", "", "", "", "");
343 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
344 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
345 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
346 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
347 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
348 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
349 ($Dat{'ele'} = $1, $Alt_unit = $2);
350 # D("ele = \"$Dat{'ele'}\"");
351 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
352 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
353 # MapSource in win xp writes YYYY, but YY in win98se.
355 defined($Dat{'year'})
356 && $Dat{'year'} =~ /\d/
357 && $Dat{'year'} < 1900
358 ) && ($Dat{'year'} += 2000);
361 } elsif (/^Track\t(.*?)\t/) {
362 $Dat{'title'} = txt_to_xml
($1);
363 $Dat{'what'} = "title";
369 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
374 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
375 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
376 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
377 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
378 $lat_d, $lat_m, $lat_s,
379 $lon_d, $lon_m, $lon_s) =
384 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
385 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
386 $Dat{'lat
'} = sprintf("%${Flat}f",
387 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
388 $Dat{'lon
'} = sprintf("%${Flon}f",
389 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
394 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
395 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
398 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
399 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
401 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
402 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
410 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
420 (\d\d) # Latitude degree
421 (\d\d) # Latitude minute
422 (\d\d\d) # Latitude minute decimals
424 (\d\d\d) # Longitude degree
425 (\d\d) # Longitude minute
426 (\d\d\d) # Longitude minute degree
432 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
433 $lon_degmin, $lon_mindec);
434 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
435 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
436 $lat_degmin, $lat_mindec, $EW,
437 $lon_deg, $lon_degmin, $lon_mindec,
438 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
439 ($2+2000, $3, $4, $5,
444 my $ep_time = timegm_nocheck(
445 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
446 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
448 $last_time = $ep_time;
449 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
450 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
451 my $tmp_lon = sprintf(
456 my $tmp_lat = sprintf("%${Flat}f",
460 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
461 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
462 $Dat{'lat
'} = $tmp_lat;
463 $Dat{'lon
'} = $tmp_lon;
466 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
467 # @020721221336__________________________________________ {{{
468 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
469 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
472 $Dat{'error
'} = "nosignal";
475 } elsif (/^xmaplog /) {
478 ($Opt{'output
-format
'} eq "csv")
479 && ($Opt{'save
-to
-file
'} eq "\n")
481 } elsif (/^Pause: /) {
482 # NOP, is here to cope with old files I’ve lying around.
483 } elsif ($Dat{'error
'} eq "desc") {
485 if (defined($Comment)) {
486 $Comment =~ s/^\s*(.*?)\s*$/$1/;
487 if ($Opt{'output
-format
'} eq "gpsml") {
488 $Dat{'desc
'} = txt_to_xml($Comment);
489 $Dat{'what
'} = "desc";
494 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
499 warn("$progname: $curr_file: Cannot open file for read: $!\n");
504 print_footer(*STDOUT);
510 my $Txt = join("", @_);
511 $Txt =~ s/<!--(.*?)-->//gs;
512 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
520 # D("print_xml_gps(\"$Orig\")\n");
521 $Str =~ s/<!--(.*?)-->//gs;
522 my $fromdate_str = "";
523 if ($Opt{'from
-date
'}) {
524 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
526 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
530 <wpt\b(.*?)>(.*?)</wpt>
535 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
536 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
538 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
539 ($Lat = postgresql_copy_safe($1));
540 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
541 ($Lon = postgresql_copy_safe($1));
542 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
543 ($Name = postgresql_copy_safe(xml_to_txt($2)));
544 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
545 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
546 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
547 ($Type = postgresql_copy_safe(xml_to_txt($2)));
548 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
549 ($Time = postgresql_copy_safe(xml_to_txt($2)));
550 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
551 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
552 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
553 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
554 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
555 ($Src = postgresql_copy_safe(xml_to_txt($2)));
556 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
557 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
559 if (length($Opt{'round
'})) {
560 if (defined($Round{'lat
'}) && length($Lat)) {
561 ($Lat = 1.0 * sprintf("%.$Round{'lat
'}f", $Lat));
563 if (defined($Round{'lon
'}) && length($Lon)) {
564 ($Lon = 1.0 * sprintf("%.$Round{'lon
'}f", $Lon));
566 if (defined($Round{'ele
'}) && $Ele ne '\N
') {
567 ($Ele = 1.0 * sprintf("%.$Round{'ele
'}f", $Ele));
571 if ($Opt{'output
-format
'} eq "pgwtab") {
585 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
589 "$Spc${Spc}UPDATE logg SET name = clname(coor) " .
590 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
591 "$Spc${Spc}UPDATE logg SET dist = cldist(coor) " .
592 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
603 <trk\b(.*?)>(.*?)</trk>
609 <name\b(.*?)>(.*?)</name>
612 $tmp_dat{'title
'} = $2;
613 $tmp_dat{'what
'} = "title";
614 $tmp_dat{'error
'} = "";
615 print_entry(%tmp_dat);
620 <trkseg\b(.*?)>(.*?)</trkseg>
626 <trkpt\b(.*?)>(.*?)</trkpt>
629 my ($attr_trkpt, $el_trkpt) =
632 'year
' => '', 'month
' => '', 'day
' => '',
633 'hour
' => '', 'min
' => '', 'sec
' => '',
636 'lat
' => '', 'lon
' => '',
642 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
643 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
644 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
648 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
649 (\d\d):?(\d\d):?([\d\.]+)Z</time>
652 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
653 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
654 ($1, $2, $3, $4, $5, $6);
670 local *OutFP = shift;
671 if ($Opt{'output
-format
'} eq "gpsml") {
673 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
677 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
678 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
679 "Datum[100]: WGS 84\n");
680 } elsif ($Opt{'output
-format
'} eq "gpx") {
682 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
684 qq{$Spc${Spc}version="1.1"\n},
685 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
686 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
687 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
688 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
689 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
692 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
694 } elsif ($Opt{'output
-format
'} eq "ps") {
695 print(OutFP ps_header(532, 6034, 533, 6040));
697 } elsif ($Opt{'output
-format
'} eq "svg") {
699 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
700 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
701 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
702 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
703 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
704 "$Spc$Spc<title></title>\n",
705 "$Spc$Spc<desc></desc>\n",
713 local *OutFP = shift;
714 if ($Opt{'output
-format
'} eq "gpsml") {
719 } elsif ($Opt{'output
-format
'} eq "gpx") {
721 "$Spc$Spc$Spc$Spc</trkseg>\n",
725 } elsif ($Opt{'output
-format
'} eq "poscount") {
726 while (my ($l_name, $l_val) = each %Poscount) {
727 $l_name =~ /^(.+?),(.+?)$/
728 && print(OutFP "$1\t$2\t$l_val\n");
730 } elsif ($Opt{'output
-format
'} eq "ps") {
736 } elsif ($Opt{'output
-format
'} eq "svg") {
737 print(OutFP "\"/>\n</svg>\n");
743 # Print a GPS entry with time, latitude, longitude and elevation in
747 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
748 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
749 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
750 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
751 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
752 my $print_time = length($Dat{'year
'}) ? 1 : 0;
754 if (!$Req{'position
'} && $Opt{'output
-format
'} eq "gpsml") {
755 $print_pos = (length($Dat{'lat
'}) || length($Dat{'lon
'})) ? 1 : 0;
757 $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
760 $Dat{'lat
'} = $Dat{'lon
'} = "";
762 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
763 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
765 # D("print_entry(\"" . join("\", \"", @_) . "\");");
768 if ($Opt{'near
'} && $print_pos) {
769 $Line .= sprintf("%s ",
770 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
773 if (length($Opt{'round
'})) {
774 for my $Tmp (qw{ lat lon ele }) {
775 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
777 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
782 if ($Opt{'output
-format
'} eq "poscount") {
783 if (!length($Dat{'error
'})) {
784 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
785 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
792 $ep_time = timegm_nocheck(
793 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
794 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
796 if ($Opt{'time-shift'}) {
797 # D("ep_time før: '$ep_time'");
798 $ep_time += $Opt{'time-shift'};
799 # D("ep_time etter: '$ep_time'");
800 ($Dat{'sec
'}, $Dat{'min
'},$Dat{'hour
'}, $Dat{'day
'},
801 $Dat{'month
'}, $Dat{'year
'}) = gmtime($ep_time);
802 $Dat{'year
'} += 1900;
805 $Dat{'epoch
'} = $ep_time;
806 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
807 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
808 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
809 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
810 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
811 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
812 if ($Opt{'chronology
'}) {
813 if ($last_time > $ep_time && !length($Dat{'error
'})) {
815 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
816 $progname, sec_to_string($last_time, "T"),
817 sec_to_readable($last_time-$ep_time),
818 sec_to_string($ep_time, "T")
820 # FIXME: Make --fix work with gpx.
821 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
822 $Dat{'error
'} = "chrono";
824 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
826 "%s: $curr_file: \"%sZ\": Duplicated time\n",
827 $progname, sec_to_string($last_time, "T")
829 # FIXME: Make --fix work with gpx.
830 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
831 $Dat{'error
'} = "duptime";
845 if ($Opt{'save
-to
-file
'} ne "\n") {
847 $print_time || return;
848 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
849 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
850 "$Opt{'save
-to
-file
'}";
851 my $file_name = $base_name;
853 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
854 $file_name = "$base_name.dup_$a";
857 die("$progname: $base_name: File already exists, and ran " .
858 "out of attempts to create unique file name\n");
860 if ($Opt{'verbose
'}) {
861 warn("$progname: $base_name: File already exists, using " .
862 "unique name \"$file_name\" instead\n");
865 if (open(ToFP, ">", $file_name)) {
874 ) || die("$progname: $file_name: Cannot write to file: $!\n");
877 if ($Opt{'output
-format
'} eq "gpsml") {
878 printf("<include>%s</include>\n",
879 txt_to_xml($file_name));
880 } elsif ($Opt{'output
-format
'} eq "gpx") {
881 printf("<!-- Saved unconverted data to \"%s\" -->\n",
882 txt_to_xml($file_name));
884 print("$progname: Saved unconverted data to \"$file_name\"\n");
888 die("$progname: $file_name: Cannot create file: $!\n");
896 if ($Dat{'what
'} eq "tp") {
898 if ($Opt{'require'}) {
899 $Req{'time'} && !$print_time && return;
900 $Req{'position
'} && !$print_pos && return;
901 $Req{'ele
'} && !$print_ele && return;
904 if ($Opt{'inside
'} || $Opt{'outside
'}) {
906 ($Dat{'lat
'} < $lat1) ||
907 ($Dat{'lat
'} > $lat2) ||
908 ($Dat{'lon
'} < $lon1) ||
909 ($Dat{'lon
'} > $lon2)
911 $Opt{'inside
'} && return;
913 $Opt{'outside
'} && return;
917 if ($Opt{'output
-format
'} eq "ps") {
924 && ($Dat{'lon
'} eq $last_lon)
925 && ($Dat{'lat
'} eq $last_lat)
927 if ($Opt{'output
-format
'} eq 'gpsml
') {
928 $Dat{'error
'} = "dup";
937 $Opt{'create
-breaks
'}
938 && $ep_time-$last_time > $PAUSE_LIMIT
941 $pause_len = $ep_time-$last_time;
942 # D("pause_len set to '$pause_len'");
946 if ($Opt{'output
-format
'} eq "gpsml") {
947 $Line .= sprintf("<pause>%s</pause>\n",
948 sec_to_readable($ep_time-$last_time));
949 } elsif ($Opt{'output
-format
'} eq "clean") {
950 $pause_len && ($Line .= "\n");
951 } elsif ($Opt{'output
-format
'} eq "csv") {
952 $Line .= sprintf("# Pause: %s\n# move\n",
953 sec_to_readable($ep_time-$last_time));
954 } elsif ($Opt{'output
-format
'} eq "xgraph") {
955 $pause_len && ($Line .= "move ");
962 # Valid data was found, send to stdout {{{
963 unless ($first_time) {
964 $first_time = $ep_time;
966 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
969 if ($Opt{'output
-format
'} eq "gpsml") {
970 if ($Dat{'what
'} eq "tp") {
971 $Dat{'format
'} = "gpsml";
972 $Line .= trackpoint(%Dat);
973 } elsif ($Dat{'what
'} =~ /^(pause|desc|title)$/) {
974 $Line .= sprintf("<%s>%s</%s>\n",
979 } elsif ($Opt{'output
-format
'} eq "pgtab") {
980 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
981 $Dat{'format
'} = "pgtab";
982 $Line .= trackpoint(%Dat);
984 } elsif ($Opt{'output
-format
'} eq "xgraph") {
985 if ($print_pos && !length($Dat{'error
'})) {
986 $Dat{'format
'} = "xgraph";
987 $Line .= trackpoint(%Dat);
989 } elsif($Opt{'output
-format
'} eq "gpstrans") {
990 if ($print_pos && !length($Dat{'error
'})) {
991 $Dat{'format
'} = "gpstrans";
992 $Line .= trackpoint(%Dat);
994 } elsif($Opt{'output
-format
'} eq "gpx") {
995 if ($Dat{'what
'} eq "tp") {
996 $Dat{'format
'} = "gpx";
997 $Line .= trackpoint(%Dat);
999 } elsif ($Opt{'output
-format
'} eq "clean") {
1000 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
1001 $Dat{'format
'} = "clean";
1002 $Line .= trackpoint(%Dat);
1004 } elsif ($Opt{'output
-format
'} eq "ps") {
1007 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
1008 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
1010 } elsif ($Opt{'output
-format
'} eq "svg") {
1012 ($last_lon == 1000) || $pause_len
1014 "$svg_start_thing<path\n",
1015 " stroke=\"blue\"\n",
1016 " stroke-width=\"0.001\"\n",
1019 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1020 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1022 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1023 if (!length($Dat{'error
'})) {
1024 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1025 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1027 } elsif ($Opt{'output
-format
'} eq "csv") {
1029 if (!length($Dat{'error
'})) {
1030 $Dat{'format
'} = "csv";
1035 : $Opt{'short
-date
'}
1036 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1037 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1038 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1039 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1043 $print_ele ? $Dat{'ele
'} : "", # Elevation
1048 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1049 # FIXME: NOP at the moment.
1051 die("$progname: \"$Opt{'output
-format
'}\": " .
1052 "Unknown output format\n");
1057 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1058 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1063 if ($Opt{'output
-format
'} eq "gpsml") {
1064 $Line = "<break/>\n$Line";
1066 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1067 && ($Line .= "move $Line");
1068 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1069 if ($Opt{'output
-format
'} eq "gpx") {
1070 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1071 "$Spc$Spc$Spc$Spc<trkseg>\n";
1077 $print_time && ($last_time = $ep_time);
1079 $last_lon = $Dat{'lon
'};
1080 $last_lat = $Dat{'lat
'};
1082 $last_line = $data_line;
1083 $svg_start_thing = "\"/>\n";
1088 # Send a Postscript header to stdout {{{
1089 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1090 my $Date = sec_to_string(time);
1092 "%!PS-Adobe-3.0 EPSF-3.0\n",
1093 "%%Creator: $rcs_id\n",
1095 "%%CreationDate: $Date\n",
1096 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1097 "%%DocumentData: Clean7Bit\n",
1100 "/bd { bind def } bind def\n",
1101 "/incompound false def\n",
1102 "/m { moveto } bd\n",
1103 "/l { lineto } bd\n",
1104 "/c { curveto } bd\n",
1105 "/F { incompound not {fill} if } bd\n",
1106 "/f { closepath F } bd\n",
1107 "/S { stroke } bd\n",
1108 "/*u { /incompound true def } bd\n",
1109 "/*U { /incompound false def f} bd\n",
1110 "/k { setcmykcolor } bd\n",
1120 # Print program version {{{
1121 for (@main::version_array) {
1128 # Send the help message to stdout {{{
1131 if ($Opt{'verbose
'}) {
1137 Converts between various GPS formats.
1139 Usage: $progname [options] [file [files [...]]]
1140 $progname -S [file [files [...]]]
1141 $progname -u [file [files [...]]]
1146 Check for broken chronology, warn about entries with an old
1149 Skip duplicated coordinates.
1151 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1153 Comment out entries which is obviously wrong. Use together with
1154 --chronology to fix those kind of errors. Does not work with GPX
1157 Used by the pgwupd format. Specifies from which date waypoints
1158 should be updated. No checks for valid date format here, let
1159 PostgreSQL take care of that. All variants it understands can be
1164 Print only trackpoints inside a rectangle specified by --pos1 and
1167 Use x as undefined value. Default: "$Udef".
1169 Add names of the three closest waypoints to the trackpoint.
1170 Unfinished and experimental, needs gpsbabel, which is called from
1171 the program as "$Cmd{'gpsbabel
'}".
1172 -o, --output-format x
1173 Use output format x:
1188 Print only trackpoints outside a rectangle specified by --pos1 and
1192 Specifies one corner where x is in "lat,lon" format (decimal
1193 degrees, negative for west or south) of area rectangle used by the
1194 --inside and --outside options.
1196 Specify requirements for trackpoints to be written. x is a string
1197 with the following flags:
1199 Print only waypoints which have an elevation.
1201 Print only waypoints which have a position.
1203 Print only waypoints which have a timestamp.
1204 -R, --round x=y[,x2=y2[...]]
1205 Round trackpoint element x to y decimals. Example:
1206 --round lat=4,lon=5,ele=1
1208 Use short date format.
1209 -S, --save-to-file x
1210 Save the unconverted data to a file with a filename starting with
1211 the timestamp of the first trackpoint. The parameter string x is
1212 added at the end of the filename. For the time being this option
1213 will ignore all other options. Note: If several files are specified
1214 on the command line, all data will be saved into only one file. This
1215 behaviour may change in the future.
1217 Create breaks in track between points with a difference more than
1218 $PAUSE_LIMIT seconds.
1219 -T x, --time-shift x
1220 Move time of trackpoint x seconds forwards or backwards. x can be a
1221 positive or negative integer.
1223 Increase level of verbosity. Can be repeated.
1225 Print version information.
1226 -w, --strip-whitespace
1227 Strip all unnecessary whitespace.
1228 -y, --double-y-scale
1229 Double Y scale (latitude) to get it right in gnuplot.
1231 Print debugging messages.
1239 # Print a status message to stderr based on verbosity level {{{
1240 my ($verbose_level, $Txt) = @_;
1242 if ($Opt{'verbose'} >= $verbose_level) {
1243 print(STDERR
"$progname: $Txt\n");
1251 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1253 # This program is free software; you can redistribute it and/or modify
1254 # it under the terms of the GNU General Public License as published by
1255 # the Free Software Foundation; either version 2 of the License, or (at
1256 # your option) any later version.
1258 # This program is distributed in the hope that it will be useful, but
1259 # WITHOUT ANY WARRANTY; without even the implied warranty of
1260 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1261 # See the GNU General Public License for more details.
1263 # You should have received a copy of the GNU General Public License
1264 # along with this program; if not, write to the Free Software
1265 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1269 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :