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 {{{
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,
68 $progname =~ s/^.*\/(.*?)$/$1/;
71 my $id_date = $rcs_id;
72 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
74 push(@main::version_array
, $rcs_id);
76 Getopt
::Long
::Configure
("bundling");
78 # Command line options {{{
80 "chronology" => \
$Opt{'chronology'},
81 "create-breaks|t" => \
$Opt{'create-breaks'},
82 "debug" => \
$Opt{'debug'},
83 "double-y-scale|y" => \
$Opt{'double-y-scale'},
84 "epoch|e" => \
$Opt{'epoch'},
85 "fix" => \
$Opt{'fix'},
86 "from-date=s" => \
$Opt{'from-date'},
87 "help|h" => \
$Opt{'help'},
88 "inside" => \
$Opt{'inside'},
89 "near" => \
$Opt{'near'},
90 "output-format|o=s" => \
$Opt{'output-format'},
91 "outside" => \
$Opt{'outside'},
92 "pos1=s" => \
$Opt{'pos1'},
93 "pos2=s" => \
$Opt{'pos2'},
94 "require|r=s" => \
$Opt{'require'},
95 "round|R=s" => \
$Opt{'round'},
96 "save-to-file|S=s" => \
$Opt{'save-to-file'},
97 "short-date|s" => \
$Opt{'short-date'},
98 "skip-dups|d" => \
$Opt{'skip-dups'},
99 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
100 "time-shift|T=i" => \
$Opt{'time-shift'},
101 "undefined|n=s" => \
$Opt{'undefined'},
102 "verbose|v+" => \
$Opt{'verbose'},
103 "version" => \
$Opt{'version'},
106 ) || die("$progname: Option error. Use -h for help.\n");
110 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
112 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
113 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
114 my $Spc = $GPST::Spc
; # FIXME
115 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
118 my ($last_lon, $last_lat, $last_line) =
119 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
120 my ($lat1, $lon1, $lat2, $lon2) =
121 (-1000, -1000, 1000, 1000);
124 'gpsbabel' => '/usr/local/bin/gpsbabel',
129 if ($Opt{'output-format'} eq "pgtab") {
130 $Opt{'require'} .= "p";
133 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
134 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
135 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
137 $Opt{'require'} =~ /[^ept]/
138 && die("$0: Unknown flag in --require (-r) value\n");
140 $Opt{'debug'} && ($Debug = 1);
141 $Opt{'help'} && usage
(0);
142 if ($Opt{'version'}) {
147 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
151 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
166 if ($Opt{'epoch'} && $Opt{'short-date'}) {
167 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
170 if ($Opt{'inside'} && $Opt{'outside'}) {
171 die("$progname: Cannot mix the --inside and --outside options\n");
174 # To avoid printing out extra "/> at the start of svg output:
175 my $svg_start_thing = "";
179 if (defined($Opt{'round'})) {
180 my $R = $Opt{'round'};
181 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
184 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
186 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
193 my $from_stdin = scalar(@ARGV) ?
0 : 1;
195 $from_stdin && push(@ARGV, "-");
197 for $curr_file (@ARGV) {
198 # Scan through stdin or specified files and send every GPS entry to
201 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
202 if (open(CurrFP
, "<$curr_file")) {
207 'year' => '', 'month' => '', 'day' => '',
208 'hour' => '', 'min' => '', 'sec' => '',
211 'lat' => '', 'lon' => '',
218 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
219 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
221 if ($Opt{'save-to-file'} ne "\n") {
222 push(@first_lines, $_);
224 s/^# error // && ($Dat{'error'} = "error");
225 s/^# ?// && ($Dat{'error'} = "desc");
227 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
228 # gpsml — The main storage format {{{
229 my ($Elem, $Props, $Data) =
231 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
232 $Elem eq "etp" && ($Dat{'error'} = $err_str);
234 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
236 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
238 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
239 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
244 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
245 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
246 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
247 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
250 } elsif (m
#^<break\b.*?/>#) {
252 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
256 } elsif (m
#^<desc\b.*?>(.*$)#s) {
257 $Dat{'what'} = "desc";
259 until ($Txt =~ m
#</desc>#s) {
262 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
267 $xml_data .= join("", <CurrFP
>);
268 if (!length($Opt{'output-format'})) {
269 $Opt{'output-format'} = "gpx";
270 print_header
(*STDOUT
);
272 read_xmlfile
($xml_data);
276 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
277 # CSV format, epoch style {{{
278 my ($ep_time, $lon_val, $lat_val, $Alt) =
280 $Dat{'epoch'} = $ep_time;
281 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
282 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
283 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
284 $Dat{'month'}++; # Urgh Ⅰ
285 $Dat{'year'} += 1900; # Urgh Ⅱ
290 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
291 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
294 # CSV format, human-readable date format {{{
295 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
296 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
297 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
303 } elsif (/^Trackpoint\t/) {
304 # 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 {{{
307 # N60.41630 E5.31675\t
308 # 09.02.2006 20:24:37 (UTC)\t
316 $Orig =~ s/[\r\n]+$//;
317 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
318 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
320 # Nødløsning for å unngå at variabler
322 "\t\t\t\t\t\t\t\t\t\t"
325 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
326 "Time_f=\"$Time_f\"\n",
327 "Alt_f=\"$Alt_f\"\n",
328 "Depth_f=\"$Depth_f\"\n",
329 "Leglength_f=\"$Leglength_f\"\n",
330 "Legtime_f=\"$Legtime_f\"\n",
331 "Legspeed_f=\"$Legspeed_f\"\n",
332 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
337 $Legtime_hour, $Legtime_min, $Legtime_sec,
338 $Legspeed, $Legspeed_unit,
340 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
341 "", "", "", "", "", "", "", "", "", "");
342 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
343 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
344 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
345 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
346 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
347 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
348 ($Dat{'ele'} = $1, $Alt_unit = $2);
349 D
("ele = \"$Dat{'ele'}\"");
350 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
351 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
352 # MapSource in win xp writes YYYY, but YY in win98se.
354 defined($Dat{'year'})
355 && $Dat{'year'} =~ /\d/
356 && $Dat{'year'} < 1900
357 ) && ($Dat{'year'} += 2000);
360 } elsif (/^Track\t(.*?)\t/) {
361 $Dat{'title'} = txt_to_xml
($1);
362 $Dat{'what'} = "title";
368 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
373 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
374 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
375 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
376 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
377 $lat_d, $lat_m, $lat_s,
378 $lon_d, $lon_m, $lon_s) =
383 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
384 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
385 $Dat{'lat
'} = sprintf("%${Flat}f",
386 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
387 $Dat{'lon
'} = sprintf("%${Flon}f",
388 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
393 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
394 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
397 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
398 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
400 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
401 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
409 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
419 (\d\d) # Latitude degree
420 (\d\d) # Latitude minute
421 (\d\d\d) # Latitude minute decimals
423 (\d\d\d) # Longitude degree
424 (\d\d) # Longitude minute
425 (\d\d\d) # Longitude minute degree
431 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
432 $lon_degmin, $lon_mindec);
433 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
434 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
435 $lat_degmin, $lat_mindec, $EW,
436 $lon_deg, $lon_degmin, $lon_mindec,
437 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
438 ($2+2000, $3, $4, $5,
443 my $ep_time = timegm_nocheck(
444 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
445 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
447 $last_time = $ep_time;
448 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
449 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
450 my $tmp_lon = sprintf(
455 my $tmp_lat = sprintf("%${Flat}f",
459 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
460 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
461 $Dat{'lat
'} = $tmp_lat;
462 $Dat{'lon
'} = $tmp_lon;
465 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
466 # @020721221336__________________________________________ {{{
467 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
468 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
471 $Dat{'error
'} = "nosignal";
474 } elsif (/^xmaplog /) {
477 ($Opt{'output
-format
'} eq "csv")
478 && ($Opt{'save
-to
-file
'} eq "\n")
480 } elsif (/^Pause: /) {
481 # NOP, is here to cope with old files I’ve lying around.
482 } elsif ($Dat{'error
'} eq "desc") {
484 if (defined($Comment)) {
485 $Comment =~ s/^\s*(.*?)\s*$/$1/;
486 if ($Opt{'output
-format
'} eq "gpsml") {
487 $Dat{'desc
'} = txt_to_xml($Comment);
488 $Dat{'what
'} = "desc";
493 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
498 warn("$progname: $curr_file: Cannot open file for read: $!\n");
503 print_footer(*STDOUT);
509 my $Txt = join("", @_);
510 $Txt =~ s/<!--(.*?)-->//gs;
511 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
519 # D("print_xml_gps(\"$Orig\")\n");
520 $Str =~ s/<!--(.*?)-->//gs;
521 my $fromdate_str = "";
522 if ($Opt{'from
-date
'}) {
523 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
525 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
529 <wpt\b(.*?)>(.*?)</wpt>
534 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
535 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
537 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
538 ($Lat = postgresql_copy_safe($1));
539 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
540 ($Lon = postgresql_copy_safe($1));
541 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
542 ($Name = postgresql_copy_safe(xml_to_txt($2)));
543 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
544 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
545 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
546 ($Type = postgresql_copy_safe(xml_to_txt($2)));
547 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
548 ($Time = postgresql_copy_safe(xml_to_txt($2)));
549 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
550 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
551 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
552 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
553 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
554 ($Src = postgresql_copy_safe(xml_to_txt($2)));
555 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
556 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
558 if ($Opt{'output
-format
'} eq "pgwtab") {
570 '\N
' # Local db id, will be defined when read into wayp_new
573 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
577 " UPDATE logg SET sted = clname(coor) " .
578 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
579 " UPDATE logg SET dist = cldist(coor) " .
580 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
591 <trk\b(.*?)>(.*?)</trk>
597 <name\b(.*?)>(.*?)</name>
600 $tmp_dat{'title
'} = $2;
601 $tmp_dat{'what
'} = "title";
602 $tmp_dat{'error
'} = "";
603 print_entry(%tmp_dat);
608 <trkseg\b(.*?)>(.*?)</trkseg>
614 <trkpt\b(.*?)>(.*?)</trkpt>
617 my ($attr_trkpt, $el_trkpt) =
620 'year
' => '', 'month
' => '', 'day
' => '',
621 'hour
' => '', 'min
' => '', 'sec
' => '',
624 'lat
' => '', 'lon
' => '',
630 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
631 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
632 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
636 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
637 (\d\d):?(\d\d):?([\d\.]+)Z</time>
640 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
641 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
642 ($1, $2, $3, $4, $5, $6);
658 local *OutFP = shift;
659 if ($Opt{'output
-format
'} eq "gpsml") {
661 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
665 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
666 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
667 "Datum[100]: WGS 84\n");
668 } elsif ($Opt{'output
-format
'} eq "gpx") {
670 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
672 qq{$Spc${Spc}version="1.1"\n},
673 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
674 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
675 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
676 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
677 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
680 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
682 } elsif ($Opt{'output
-format
'} eq "ps") {
683 print(OutFP ps_header(532, 6034, 533, 6040));
685 } elsif ($Opt{'output
-format
'} eq "svg") {
687 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
688 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
689 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
690 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
691 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
692 "$Spc$Spc<title></title>\n",
693 "$Spc$Spc<desc></desc>\n",
701 local *OutFP = shift;
702 if ($Opt{'output
-format
'} eq "gpsml") {
707 } elsif ($Opt{'output
-format
'} eq "gpx") {
709 "$Spc$Spc$Spc$Spc</trkseg>\n",
713 } elsif ($Opt{'output
-format
'} eq "poscount") {
714 while (my ($l_name, $l_val) = each %Poscount) {
715 $l_name =~ /^(.+?),(.+?)$/
716 && print(OutFP "$1\t$2\t$l_val\n");
718 } elsif ($Opt{'output
-format
'} eq "ps") {
724 } elsif ($Opt{'output
-format
'} eq "svg") {
725 print(OutFP "\"/>\n</svg>\n");
731 # Print a GPS entry with time, latitude, longitude and elevation in
735 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
736 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
737 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
738 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
739 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
740 my $print_time = length($Dat{'year
'}) ? 1 : 0;
742 if (!$Req{'position
'} && $Opt{'output
-format
'} eq "gpsml") {
743 $print_pos = (length($Dat{'lat
'}) || length($Dat{'lon
'})) ? 1 : 0;
745 $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
748 $Dat{'lat
'} = $Dat{'lon
'} = "";
750 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
751 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
753 D("print_entry(\"" . join("\", \"", @_) . "\");");
756 if ($Opt{'near
'} && $print_pos) {
757 $Line .= sprintf("%s ",
758 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
761 if (length($Opt{'round
'})) {
762 for my $Tmp (qw{ lat lon ele }) {
763 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
765 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
770 if ($Opt{'output
-format
'} eq "poscount") {
771 if (!length($Dat{'error
'})) {
772 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
773 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
780 $ep_time = timegm_nocheck(
781 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
782 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
784 if ($Opt{'time-shift'}) {
785 D("ep_time før: '$ep_time'");
786 $ep_time += $Opt{'time-shift'};
787 D("ep_time etter: '$ep_time'");
788 ($Dat{'sec
'}, $Dat{'min
'},$Dat{'hour
'}, $Dat{'day
'},
789 $Dat{'month
'}, $Dat{'year
'}) = gmtime($ep_time);
790 $Dat{'year
'} += 1900;
793 $Dat{'epoch
'} = $ep_time;
794 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
795 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
796 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
797 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
798 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
799 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
800 if ($Opt{'chronology
'}) {
801 if ($last_time > $ep_time && !length($Dat{'error
'})) {
803 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
804 $progname, sec_to_string($last_time, "T"),
805 sec_to_readable($last_time-$ep_time),
806 sec_to_string($ep_time, "T")
808 # FIXME: Make --fix work with gpx.
809 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
810 $Dat{'error
'} = "chrono";
812 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
814 "%s: $curr_file: \"%sZ\": Duplicated time\n",
815 $progname, sec_to_string($last_time, "T")
817 # FIXME: Make --fix work with gpx.
818 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
819 $Dat{'error
'} = "duptime";
833 if ($Opt{'save
-to
-file
'} ne "\n") {
835 $print_time || return;
836 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
837 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
838 "$Opt{'save
-to
-file
'}";
839 my $file_name = $base_name;
841 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
842 $file_name = "$base_name.dup_$a";
845 die("$progname: $base_name: File already exists, and ran " .
846 "out of attempts to create unique file name\n");
848 if ($Opt{'verbose
'}) {
849 warn("$progname: $base_name: File already exists, using " .
850 "unique name \"$file_name\" instead\n");
853 if (open(ToFP, ">", $file_name)) {
862 ) || die("$progname: $file_name: Cannot write to file: $!\n");
865 if ($Opt{'output
-format
'} eq "gpsml") {
866 printf("<include>%s</include>\n",
867 txt_to_xml($file_name));
868 } elsif ($Opt{'output
-format
'} eq "gpx") {
869 printf("<!-- Saved unconverted data to \"%s\" -->\n",
870 txt_to_xml($file_name));
872 print("$progname: Saved unconverted data to \"$file_name\"\n");
876 die("$progname: $file_name: Cannot create file: $!\n");
884 if ($Dat{'what
'} eq "tp") {
886 if ($Opt{'require'}) {
887 $Req{'time'} && !$print_time && return;
888 $Req{'position
'} && !$print_pos && return;
889 $Req{'ele
'} && !$print_ele && return;
892 if ($Opt{'inside
'} || $Opt{'outside
'}) {
894 ($Dat{'lat
'} < $lat1) ||
895 ($Dat{'lat
'} > $lat2) ||
896 ($Dat{'lon
'} < $lon1) ||
897 ($Dat{'lon
'} > $lon2)
899 $Opt{'inside
'} && return;
901 $Opt{'outside
'} && return;
905 if ($Opt{'output
-format
'} eq "ps") {
912 && ($Dat{'lon
'} eq $last_lon)
913 && ($Dat{'lat
'} eq $last_lat)
915 if ($Opt{'output
-format
'} eq 'gpsml
') {
916 $Dat{'error
'} = "dup";
925 $Opt{'create
-breaks
'}
926 && $ep_time-$last_time > $PAUSE_LIMIT
929 $pause_len = $ep_time-$last_time;
930 D("pause_len set to '$pause_len'");
934 if ($Opt{'output
-format
'} eq "gpsml") {
935 $Line .= sprintf("<pause>%s</pause>\n",
936 sec_to_readable($ep_time-$last_time));
937 } elsif ($Opt{'output
-format
'} eq "clean") {
938 $pause_len && ($Line .= "\n");
939 } elsif ($Opt{'output
-format
'} eq "csv") {
940 $Line .= sprintf("# Pause: %s\n# move\n",
941 sec_to_readable($ep_time-$last_time));
942 } elsif ($Opt{'output
-format
'} eq "xgraph") {
943 $pause_len && ($Line .= "move ");
950 # Valid data was found, send to stdout {{{
951 unless ($first_time) {
952 $first_time = $ep_time;
954 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
957 if ($Opt{'output
-format
'} eq "gpsml") {
958 if ($Dat{'what
'} eq "tp") {
959 $Dat{'format
'} = "gpsml";
960 $Line .= trackpoint(%Dat);
961 } elsif ($Dat{'what
'} =~ /^(pause|desc|title)$/) {
962 $Line .= sprintf("<%s>%s</%s>\n",
967 } elsif ($Opt{'output
-format
'} eq "pgtab") {
968 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
969 $Dat{'format
'} = "pgtab";
970 $Line .= trackpoint(%Dat);
972 } elsif ($Opt{'output
-format
'} eq "xgraph") {
973 if ($print_pos && !length($Dat{'error
'})) {
974 $Dat{'format
'} = "xgraph";
975 $Line .= trackpoint(%Dat);
977 } elsif($Opt{'output
-format
'} eq "gpstrans") {
978 if ($print_pos && !length($Dat{'error
'})) {
979 $Dat{'format
'} = "gpstrans";
980 $Line .= trackpoint(%Dat);
982 } elsif($Opt{'output
-format
'} eq "gpx") {
983 if ($Dat{'what
'} eq "tp") {
984 $Dat{'format
'} = "gpx";
985 $Line .= trackpoint(%Dat);
987 } elsif ($Opt{'output
-format
'} eq "clean") {
988 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
989 $Dat{'format
'} = "clean";
990 $Line .= trackpoint(%Dat);
992 } elsif ($Opt{'output
-format
'} eq "ps") {
995 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
996 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
998 } elsif ($Opt{'output
-format
'} eq "svg") {
1000 ($last_lon == 1000) || $pause_len
1002 "$svg_start_thing<path\n",
1003 " stroke=\"blue\"\n",
1004 " stroke-width=\"0.001\"\n",
1007 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1008 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1010 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1011 if (!length($Dat{'error
'})) {
1012 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1013 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1015 } elsif ($Opt{'output
-format
'} eq "csv") {
1017 if (!length($Dat{'error
'})) {
1018 $Dat{'format
'} = "csv";
1023 : $Opt{'short
-date
'}
1024 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1025 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1026 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1027 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1031 $print_ele ? $Dat{'ele
'} : "", # Elevation
1036 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1037 # FIXME: NOP at the moment.
1039 die("$progname: \"$Opt{'output
-format
'}\": " .
1040 "Unknown output format\n");
1045 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1046 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1051 if ($Opt{'output
-format
'} eq "gpsml") {
1052 $Line = "<break/>\n$Line";
1054 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1055 && ($Line .= "move $Line");
1056 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1057 if ($Opt{'output
-format
'} eq "gpx") {
1058 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1059 "$Spc$Spc$Spc$Spc<trkseg>\n";
1065 $print_time && ($last_time = $ep_time);
1067 $last_lon = $Dat{'lon
'};
1068 $last_lat = $Dat{'lat
'};
1070 $last_line = $data_line;
1071 $svg_start_thing = "\"/>\n";
1076 # Send a Postscript header to stdout {{{
1077 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1078 my $Date = sec_to_string(time);
1080 "%!PS-Adobe-3.0 EPSF-3.0\n",
1081 "%%Creator: $rcs_id\n",
1083 "%%CreationDate: $Date\n",
1084 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1085 "%%DocumentData: Clean7Bit\n",
1088 "/bd { bind def } bind def\n",
1089 "/incompound false def\n",
1090 "/m { moveto } bd\n",
1091 "/l { lineto } bd\n",
1092 "/c { curveto } bd\n",
1093 "/F { incompound not {fill} if } bd\n",
1094 "/f { closepath F } bd\n",
1095 "/S { stroke } bd\n",
1096 "/*u { /incompound true def } bd\n",
1097 "/*U { /incompound false def f} bd\n",
1098 "/k { setcmykcolor } bd\n",
1108 # Print program version {{{
1109 for (@main::version_array) {
1116 # Send the help message to stdout {{{
1119 if ($Opt{'verbose
'}) {
1125 Converts between various GPS formats.
1127 Usage: $progname [options] [file [files [...]]]
1128 $progname -S [file [files [...]]]
1129 $progname -u [file [files [...]]]
1134 Check for broken chronology, warn about entries with an old
1137 Skip duplicated coordinates.
1139 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1141 Comment out entries which is obviously wrong. Use together with
1142 --chronology to fix those kind of errors. Does not work with GPX
1145 Used by the pgwupd format. Specifies from which date waypoints
1146 should be updated. No checks for valid date format here, let
1147 PostgreSQL take care of that. All variants it understands can be
1152 Print only trackpoints inside a rectangle specified by --pos1 and
1155 Use x as undefined value. Default: "$Udef".
1157 Add names of the three closest waypoints to the trackpoint.
1158 Unfinished and experimental, needs gpsbabel, which is called from
1159 the program as "$Cmd{'gpsbabel
'}".
1160 -o, --output-format x
1161 Use output format x:
1176 Print only trackpoints outside a rectangle specified by --pos1 and
1180 Specifies one corner where x is in "lat,lon" format (decimal
1181 degrees, negative for west or south) of area rectangle used by the
1182 --inside and --outside options.
1184 Specify requirements for trackpoints to be written. x is a string
1185 with the following flags:
1187 Print only waypoints which have an elevation.
1189 Print only waypoints which have a position.
1191 Print only waypoints which have a timestamp.
1192 -R, --round x=y[,x2=y2[...]]
1193 Round trackpoint element x to y decimals. Example:
1194 --round lat=4,lon=5,ele=1
1196 Use short date format.
1197 -S, --save-to-file x
1198 Save the unconverted data to a file with a filename starting with
1199 the timestamp of the first trackpoint. The parameter string x is
1200 added at the end of the filename. For the time being this option
1201 will ignore all other options. Note: If several files are specified
1202 on the command line, all data will be saved into only one file. This
1203 behaviour may change in the future.
1205 Create breaks in track between points with a difference more than
1206 $PAUSE_LIMIT seconds.
1207 -T x, --time-shift x
1208 Move time of trackpoint x seconds forwards or backwards. x can be a
1209 positive or negative integer.
1211 Increase level of verbosity. Can be repeated.
1213 Print version information.
1214 -w, --strip-whitespace
1215 Strip all unnecessary whitespace.
1216 -y, --double-y-scale
1217 Double Y scale (latitude) to get it right in gnuplot.
1219 Print debugging messages.
1227 # Print a status message to stderr based on verbosity level {{{
1228 my ($verbose_level, $Txt) = @_;
1230 if ($Opt{'verbose'} >= $verbose_level) {
1231 print(STDERR
"$progname: $Txt\n");
1239 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1241 # This program is free software; you can redistribute it and/or modify
1242 # it under the terms of the GNU General Public License as published by
1243 # the Free Software Foundation; either version 2 of the License, or (at
1244 # your option) any later version.
1246 # This program is distributed in the hope that it will be useful, but
1247 # WITHOUT ANY WARRANTY; without even the implied warranty of
1248 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1249 # See the GNU General Public License for more details.
1251 # You should have received a copy of the GNU General Public License
1252 # along with this program; if not, write to the Free Software
1253 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1257 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :