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 3 or later, see end of
11 # file for legal stuff.
12 #=======================================================================
17 use Time
::Local qw
{ timegm_nocheck
};
20 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
34 # Initial values for command line arguments {{{
39 'double-y-scale' => 0,
45 'output-format' => "gpsml",
51 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
54 'strip-whitespace' => 0,
64 $progname =~ s/^.*\/(.*?)$/$1/;
65 our $VERSION = "0.00";
67 Getopt
::Long
::Configure
("bundling");
69 # Command line options {{{
71 "chronology" => \
$Opt{'chronology'},
72 "create-breaks|t" => \
$Opt{'create-breaks'},
73 "debug" => \
$Opt{'debug'},
74 "double-y-scale|y" => \
$Opt{'double-y-scale'},
75 "epoch|e" => \
$Opt{'epoch'},
76 "fix" => \
$Opt{'fix'},
77 "from-date=s" => \
$Opt{'from-date'},
78 "help|h" => \
$Opt{'help'},
79 "inside" => \
$Opt{'inside'},
80 "output-format|o=s" => \
$Opt{'output-format'},
81 "outside" => \
$Opt{'outside'},
82 "pos1=s" => \
$Opt{'pos1'},
83 "pos2=s" => \
$Opt{'pos2'},
84 "require|r=s" => \
$Opt{'require'},
85 "round|R=s" => \
$Opt{'round'},
86 "save-to-file|S=s" => \
$Opt{'save-to-file'},
87 "short-date|s" => \
$Opt{'short-date'},
88 "skip-dups|d" => \
$Opt{'skip-dups'},
89 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
90 "time-shift|T=i" => \
$Opt{'time-shift'},
91 "undefined|n=s" => \
$Opt{'undefined'},
92 "verbose|v+" => \
$Opt{'verbose'},
93 "version" => \
$Opt{'version'},
96 ) || die("$progname: Option error. Use -h for help.\n");
100 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
102 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
103 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
104 my $Spc = $GPST::Spc
; # FIXME
105 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
108 my ($last_lon, $last_lat, $last_line) =
109 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
110 my ($lat1, $lon1, $lat2, $lon2) =
111 (-1000, -1000, 1000, 1000);
115 if ($Opt{'output-format'} =~ /^(gpx|pgtab)$/) {
116 $Opt{'require'} .= "p";
119 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
120 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
121 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
123 $Opt{'require'} =~ /[^ept]/
124 && die("$0: Unknown flag in --require (-r) value\n");
126 $Opt{'debug'} && ($Debug = 1);
127 $Opt{'help'} && usage
(0);
128 if ($Opt{'version'}) {
133 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
137 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
152 if ($Opt{'epoch'} && $Opt{'short-date'}) {
153 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
156 if ($Opt{'inside'} && $Opt{'outside'}) {
157 die("$progname: Cannot mix the --inside and --outside options\n");
160 # To avoid printing out extra "/> at the start of svg output:
161 my $svg_start_thing = "";
165 if (defined($Opt{'round'})) {
166 my $R = $Opt{'round'};
167 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
170 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
172 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
178 my $from_stdin = scalar(@ARGV) ?
0 : 1;
180 $from_stdin && push(@ARGV, "-");
182 for my $curr_file (@ARGV) {
183 # Scan through stdin or specified files and send every GPS entry to
186 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
187 if (open(my $curr_fp, "<$curr_file")) {
192 'year' => '', 'month' => '', 'day' => '',
193 'hour' => '', 'min' => '', 'sec' => '',
196 'lat' => '', 'lon' => '',
201 'curr_file' => $curr_file,
204 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
205 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
207 if ($Opt{'save-to-file'} ne "\n") {
208 push(@first_lines, $_);
210 s/^# error // && ($Dat{'error'} = "error");
211 s/^# ?// && ($Dat{'error'} = "desc");
213 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
214 # gpsml — The main storage format {{{
215 my ($Elem, $Props, $Data) =
217 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
218 $Elem eq "etp" && ($Dat{'error'} = $err_str);
220 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
222 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
224 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
225 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
230 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
231 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
232 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
233 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
236 } elsif (m
#^<break\b.*?/>#) {
238 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
242 } elsif (m
#^<desc\b.*?>(.*$)#s) {
243 $Dat{'what'} = "desc";
245 until ($Txt =~ m
#</desc>#s) {
248 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
253 $xml_data .= join("", <$curr_fp>);
254 if (!length($Opt{'output-format'})) {
255 $Opt{'output-format'} = "gpx";
256 print_header
(*STDOUT
);
258 read_xmlfile
($xml_data);
262 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
263 # CSV format, epoch style {{{
264 my ($ep_time, $lon_val, $lat_val, $Alt) =
266 $Dat{'epoch'} = $ep_time;
267 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
268 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
269 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
270 $Dat{'month'}++; # Urgh Ⅰ
271 $Dat{'year'} += 1900; # Urgh Ⅱ
276 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
277 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
280 # CSV format, human-readable date format {{{
281 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
282 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
283 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
289 } elsif (/^Trackpoint\t/) {
290 # 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 {{{
293 # N60.41630 E5.31675\t
294 # 09.02.2006 20:24:37 (UTC)\t
302 $Orig =~ s/[\r\n]+$//;
303 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
304 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
306 # Nødløsning for å unngå at variabler
308 "\t\t\t\t\t\t\t\t\t\t"
311 # "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
312 # "Time_f=\"$Time_f\"\n",
313 # "Alt_f=\"$Alt_f\"\n",
314 # "Depth_f=\"$Depth_f\"\n",
315 # "Leglength_f=\"$Leglength_f\"\n",
316 # "Legtime_f=\"$Legtime_f\"\n",
317 # "Legspeed_f=\"$Legspeed_f\"\n",
318 # "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
323 $Legtime_hour, $Legtime_min, $Legtime_sec,
324 $Legspeed, $Legspeed_unit,
326 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
327 "", "", "", "", "", "", "", "", "", "");
328 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
329 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
330 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
331 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
332 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
333 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
334 ($Dat{'ele'} = $1, $Alt_unit = $2);
335 # D("ele = \"$Dat{'ele'}\"");
336 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
337 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
338 # MapSource in win xp writes YYYY, but YY in win98se.
340 defined($Dat{'year'})
341 && $Dat{'year'} =~ /\d/
342 && $Dat{'year'} < 1900
343 ) && ($Dat{'year'} += 2000);
346 } elsif (/^Track\t(.*?)\t/) {
347 $Dat{'title'} = txt_to_xml
($1);
348 $Dat{'what'} = "title";
354 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
359 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
360 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
361 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
362 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
363 $lat_d, $lat_m, $lat_s,
364 $lon_d, $lon_m, $lon_s) =
369 $Dat{'lat
'} = 1.0*($lat_d+($lat_m/60)+($lat_s/3600));
370 $Dat{'lon
'} = 1.0*$lon_d+($lon_m/60)+($lon_s/3600);
375 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
376 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
379 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
380 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
382 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
383 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
391 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
401 (\d\d) # Latitude degree
402 (\d\d) # Latitude minute
403 (\d\d\d) # Latitude minute decimals
405 (\d\d\d) # Longitude degree
406 (\d\d) # Longitude minute
407 (\d\d\d) # Longitude minute degree
413 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
414 $lon_degmin, $lon_mindec);
415 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
416 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
417 $lat_degmin, $lat_mindec, $EW,
418 $lon_deg, $lon_degmin, $lon_mindec,
419 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
420 ($2+2000, $3, $4, $5,
425 my $ep_time = timegm_nocheck(
426 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
427 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
429 $last_time = $ep_time;
430 my $tmp_lon = $lon_deg + $lon_degmin/60 + $lon_mindec/60000;
431 my $tmp_lat = $lat_deg + $lat_degmin/60 + $lat_mindec/60000;
432 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
433 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
434 $Dat{'lat
'} = $tmp_lat;
435 $Dat{'lon
'} = $tmp_lon;
438 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
439 # @020721221336__________________________________________ {{{
440 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
441 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
444 $Dat{'error
'} = "nosignal";
447 } elsif (/^xmaplog /) {
450 ($Opt{'output
-format
'} eq "csv")
451 && ($Opt{'save
-to
-file
'} eq "\n")
453 } elsif (/^Pause: /) {
454 # NOP, is here to cope with old files I’ve lying around.
455 } elsif ($Dat{'error
'} eq "desc") {
457 if (defined($Comment)) {
458 $Comment =~ s/^\s*(.*?)\s*$/$1/;
459 if ($Opt{'output
-format
'} eq "gpsml") {
460 $Dat{'desc
'} = txt_to_xml($Comment);
461 $Dat{'what
'} = "desc";
466 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
471 warn("$progname: $curr_file: Cannot open file for read: $!\n");
476 print_footer(*STDOUT);
482 my $Txt = join("", @_);
483 $Txt =~ s/<!--(.*?)-->//gs;
484 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
492 # D("print_xml_gps(\"$Orig\")\n");
493 $Str =~ s/<!--(.*?)-->//gs;
494 my $fromdate_str = "";
495 if ($Opt{'from
-date
'}) {
496 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
498 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
502 <wpt\b(.*?)>(.*?)</wpt>
507 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
508 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
510 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
511 ($Lat = postgresql_copy_safe($1));
512 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
513 ($Lon = postgresql_copy_safe($1));
514 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
515 ($Name = postgresql_copy_safe(xml_to_txt($2)));
516 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
517 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
518 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
519 ($Type = postgresql_copy_safe(xml_to_txt($2)));
520 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
521 ($Time = postgresql_copy_safe(xml_to_txt($2)));
522 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
523 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
524 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
525 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
526 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
527 ($Src = postgresql_copy_safe(xml_to_txt($2)));
528 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
529 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
531 if (length($Opt{'round
'})) {
532 if (defined($Round{'lat
'}) && length($Lat)) {
533 ($Lat = 1.0 * sprintf("%.$Round{'lat
'}f", $Lat));
535 if (defined($Round{'lon
'}) && length($Lon)) {
536 ($Lon = 1.0 * sprintf("%.$Round{'lon
'}f", $Lon));
538 if (defined($Round{'ele
'}) && $Ele ne '\N
') {
539 ($Ele = 1.0 * sprintf("%.$Round{'ele
'}f", $Ele));
543 if ($Opt{'output
-format
'} eq "pgwtab") {
557 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
561 "$Spc${Spc}UPDATE logg SET name = clname(coor) " .
562 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
563 "$Spc${Spc}UPDATE logg SET dist = cldist(coor) " .
564 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
575 <trk\b(.*?)>(.*?)</trk>
581 <name\b(.*?)>(.*?)</name>
584 $tmp_dat{'title
'} = $2;
585 $tmp_dat{'what
'} = "title";
586 $tmp_dat{'error
'} = "";
587 print_entry(%tmp_dat);
592 <trkseg\b(.*?)>(.*?)</trkseg>
598 <trkpt\b(.*?)>(.*?)</trkpt>
601 my ($attr_trkpt, $el_trkpt) =
604 'year
' => '', 'month
' => '', 'day
' => '',
605 'hour
' => '', 'min
' => '', 'sec
' => '',
608 'lat
' => '', 'lon
' => '',
614 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
615 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
616 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
620 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
621 (\d\d):?(\d\d):?([\d\.]+)Z</time>
624 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
625 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
626 ($1, $2, $3, $4, $5, $6);
643 if ($Opt{'output
-format
'} eq "gpsml") {
644 print($out_fp join("",
645 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
649 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
650 print($out_fp "Format: DMS UTC Offset: 0.00 hrs " .
651 "Datum[100]: WGS 84\n");
652 } elsif ($Opt{'output
-format
'} eq "gpx") {
653 print($out_fp join("",
654 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
656 qq{$Spc${Spc}version="1.1"\n},
657 qq{$Spc${Spc}creator="gpst - http://sunny256.github.com/gpstools/"\n},
658 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
659 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
660 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
661 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
664 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
666 } elsif ($Opt{'output
-format
'} eq "ps") {
667 print($out_fp ps_header(532, 6034, 533, 6040));
668 print($out_fp "*u\n");
669 } elsif ($Opt{'output
-format
'} eq "svg") {
670 print($out_fp join("",
671 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
672 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
673 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
674 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
675 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
676 "$Spc$Spc<title></title>\n",
677 "$Spc$Spc<desc></desc>\n",
686 if ($Opt{'output
-format
'} eq "gpsml") {
687 print($out_fp join("",
691 } elsif ($Opt{'output
-format
'} eq "gpx") {
692 print($out_fp join("",
693 "$Spc$Spc$Spc$Spc</trkseg>\n",
697 } elsif ($Opt{'output
-format
'} eq "poscount") {
698 while (my ($l_name, $l_val) = each %Poscount) {
699 $l_name =~ /^(.+?),(.+?)$/
700 && print($out_fp "$1\t$2\t$l_val\n");
702 } elsif ($Opt{'output
-format
'} eq "ps") {
703 print($out_fp join("",
708 } elsif ($Opt{'output
-format
'} eq "svg") {
709 print($out_fp "\"/>\n</svg>\n");
715 # Print a GPS entry with time, latitude, longitude and elevation in
719 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
720 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
721 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
722 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
723 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
724 my $print_time = length($Dat{'year
'}) ? 1 : 0;
726 if (!$Req{'position
'} && $Opt{'output
-format
'} eq "gpsml") {
727 $print_pos = (length($Dat{'lat
'}) || length($Dat{'lon
'})) ? 1 : 0;
729 $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
732 $Dat{'lat
'} = $Dat{'lon
'} = "";
734 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
735 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
737 # D("print_entry(\"" . join("\", \"", @_) . "\");");
740 if (length($Opt{'round
'})) {
741 for my $Tmp (qw{ lat lon ele }) {
742 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
744 ($Dat{$Tmp} = num_expand(1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp})));
749 if ($Opt{'output
-format
'} eq "poscount") {
750 # FIXME: Sort output in some way
751 if (!length($Dat{'error
'})) {
752 $Dat{'lat
'} = num_expand($Dat{'lat
'});
753 $Dat{'lon
'} = num_expand($Dat{'lon
'});
754 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
755 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
762 $ep_time = timegm_nocheck(
763 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
764 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
766 if ($Opt{'time-shift'}) {
767 # D("ep_time før: '$ep_time'");
768 $ep_time += $Opt{'time-shift'};
769 # D("ep_time etter: '$ep_time'");
770 ($Dat{'sec
'}, $Dat{'min
'},$Dat{'hour
'}, $Dat{'day
'},
771 $Dat{'month
'}, $Dat{'year
'}) = gmtime($ep_time);
772 $Dat{'year
'} += 1900;
775 $Dat{'epoch
'} = $ep_time;
776 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
777 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
778 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
779 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
780 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
781 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
782 if ($Opt{'chronology
'}) {
783 if ($last_time > $ep_time && !length($Dat{'error
'})) {
785 "%s: $Dat{'curr_file
'}: \"%sZ\": Next date is %s in the past (%sZ)\n",
786 $progname, sec_to_string($last_time, "T"),
787 sec_to_readable($last_time-$ep_time),
788 sec_to_string($ep_time, "T")
790 # FIXME: Make --fix work with gpx.
791 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
792 $Dat{'error
'} = "chrono";
794 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
796 "%s: $Dat{'curr_file
'}: \"%sZ\": Duplicated time\n",
797 $progname, sec_to_string($last_time, "T")
799 # FIXME: Make --fix work with gpx.
800 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
801 $Dat{'error
'} = "duptime";
815 if ($Opt{'save
-to
-file
'} ne "\n") {
817 $print_time || return;
818 my $base_name = "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
819 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z" .
820 "$Opt{'save
-to
-file
'}";
821 my $file_name = $base_name;
823 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
824 $file_name = "$base_name.dup_$a";
827 die("$progname: $base_name: File already exists, and ran " .
828 "out of attempts to create unique file name\n");
830 if ($Opt{'verbose
'}) {
831 warn("$progname: $base_name: File already exists, using " .
832 "unique name \"$file_name\" instead\n");
835 if (open(my $to_fp, ">", $file_name)) {
836 print_header(*$to_fp);
844 ) || die("$progname: $file_name: Cannot write to file: $!\n");
845 print_footer(*$to_fp);
847 if ($Opt{'output
-format
'} eq "gpsml") {
848 printf("<include>%s</include>\n",
849 txt_to_xml($file_name));
850 } elsif ($Opt{'output
-format
'} eq "gpx") {
851 printf("<!-- Saved unconverted data to \"%s\" -->\n",
852 txt_to_xml($file_name));
854 print("$progname: Saved unconverted data to \"$file_name\"\n");
858 die("$progname: $file_name: Cannot create file: $!\n");
866 if ($Dat{'what
'} eq "tp") {
868 if ($Opt{'require'}) {
869 $Req{'time'} && !$print_time && return;
870 $Req{'position
'} && !$print_pos && return;
871 $Req{'ele
'} && !$print_ele && return;
874 if ($Opt{'inside
'} || $Opt{'outside
'}) {
876 ($Dat{'lat
'} < $lat1) ||
877 ($Dat{'lat
'} > $lat2) ||
878 ($Dat{'lon
'} < $lon1) ||
879 ($Dat{'lon
'} > $lon2)
881 $Opt{'inside
'} && return;
883 $Opt{'outside
'} && return;
887 if ($Opt{'output
-format
'} eq "ps") {
894 && ($Dat{'lon
'} eq $last_lon)
895 && ($Dat{'lat
'} eq $last_lat)
897 if ($Opt{'output
-format
'} eq 'gpsml
') {
898 $Dat{'error
'} = "dup";
907 $Opt{'create
-breaks
'}
908 && $ep_time-$last_time > $PAUSE_LIMIT
911 $pause_len = $ep_time-$last_time;
912 # D("pause_len set to '$pause_len'");
915 $Line .= pause_entry($pause_len, $ep_time, $last_time);
920 # Valid data was found, send to stdout {{{
921 unless ($first_time) {
922 $first_time = $ep_time;
924 $Line .= gen_entry($print_pos, $pause_len, $print_time, $ep_time, $print_ele, %Dat);
928 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
929 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
934 if ($Opt{'output
-format
'} eq "gpsml") {
935 $Line = "<break/>\n$Line";
937 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
938 && ($Line .= "move $Line");
939 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
940 if ($Opt{'output
-format
'} eq "gpx") {
941 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
942 "$Spc$Spc$Spc$Spc<trkseg>\n";
948 $print_time && ($last_time = $ep_time);
950 $last_lon = $Dat{'lon
'};
951 $last_lat = $Dat{'lat
'};
953 $last_line = $data_line;
954 $svg_start_thing = "\"/>\n";
959 # Generate trackpoint entry, calls trackpoint() {{{
960 my ($print_pos, $pause_len, $print_time, $ep_time, $print_ele, %Dat) = @_;
962 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
965 if ($Opt{'output
-format
'} eq "gpsml") {
966 if ($Dat{'what
'} eq "tp") {
967 $Dat{'format
'} = "gpsml";
968 $Line .= trackpoint(%Dat);
969 } elsif ($Dat{'what
'} =~ /^(pause|desc|title)$/) {
970 $Line .= sprintf("<%s>%s</%s>\n",
975 } elsif ($Opt{'output
-format
'} eq "pgtab") {
976 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
977 $Dat{'format
'} = "pgtab";
978 $Line .= trackpoint(%Dat);
980 } elsif ($Opt{'output
-format
'} eq "xgraph") {
981 if ($print_pos && !length($Dat{'error
'})) {
982 $Dat{'format
'} = "xgraph";
983 $Line .= trackpoint(%Dat);
985 } elsif($Opt{'output
-format
'} eq "gpstrans") {
986 if ($print_pos && !length($Dat{'error
'})) {
987 $Dat{'format
'} = "gpstrans";
988 $Line .= trackpoint(%Dat);
990 } elsif($Opt{'output
-format
'} eq "gpx") {
991 if ($Dat{'what
'} eq "tp") {
992 $Dat{'format
'} = "gpx";
993 $Line .= trackpoint(%Dat);
995 } elsif ($Opt{'output
-format
'} eq "clean") {
996 if ($Dat{'what
'} eq "tp" && !length($Dat{'error
'})) {
997 $Dat{'format
'} = "clean";
998 $Line .= trackpoint(%Dat);
1000 } elsif ($Opt{'output
-format
'} eq "ps") {
1003 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
1004 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
1006 } elsif ($Opt{'output
-format
'} eq "svg") {
1008 ($last_lon == 1000) || $pause_len
1010 "$svg_start_thing<path\n",
1011 " stroke=\"blue\"\n",
1012 " stroke-width=\"0.001\"\n",
1015 "M $Dat{'lon
'} $Dat{'lat
'}\n")
1016 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
1018 } elsif ($Opt{'output
-format
'} eq "ygraph") {
1019 if (!length($Dat{'error
'})) {
1020 $Dat{'lat
'} = num_expand($Dat{'lat
'});
1021 $Dat{'lon
'} = num_expand($Dat{'lon
'});
1022 $Dat{'ele
'} = num_expand($Dat{'ele
'});
1023 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1024 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
1026 } elsif ($Opt{'output
-format
'} eq "csv") {
1028 if (!length($Dat{'error
'})) {
1029 $Dat{'format
'} = "csv";
1030 $Dat{'lat
'} = num_expand($Dat{'lat
'});
1031 $Dat{'lon
'} = num_expand($Dat{'lon
'});
1032 $Dat{'ele
'} = num_expand($Dat{'ele
'});
1037 : $Opt{'short
-date
'}
1038 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1039 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1040 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1041 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1045 $print_ele ? $Dat{'ele
'} : "", # Elevation
1050 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1051 # FIXME: NOP at the moment.
1053 die("$progname: \"$Opt{'output
-format
'}\": " .
1054 "Unknown output format\n");
1062 my ($pause_len, $ep_time, $last_time) = @_;
1065 if ($Opt{'output
-format
'} eq "gpsml") {
1066 $Line .= sprintf("<pause>%s</pause>\n",
1067 sec_to_readable($ep_time-$last_time));
1068 } elsif ($Opt{'output
-format
'} eq "clean") {
1069 $pause_len && ($Line .= "\n");
1070 } elsif ($Opt{'output
-format
'} eq "csv") {
1071 $Line .= sprintf("# Pause: %s\n# move\n",
1072 sec_to_readable($ep_time-$last_time));
1073 } elsif ($Opt{'output
-format
'} eq "xgraph") {
1074 $pause_len && ($Line .= "move ");
1082 # Send a Postscript header to stdout {{{
1083 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1084 my $Date = sec_to_string(time);
1086 "%!PS-Adobe-3.0 EPSF-3.0\n",
1087 "%%Creator: gpst\n",
1089 "%%CreationDate: $Date\n",
1090 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1091 "%%DocumentData: Clean7Bit\n",
1094 "/bd { bind def } bind def\n",
1095 "/incompound false def\n",
1096 "/m { moveto } bd\n",
1097 "/l { lineto } bd\n",
1098 "/c { curveto } bd\n",
1099 "/F { incompound not {fill} if } bd\n",
1100 "/f { closepath F } bd\n",
1101 "/S { stroke } bd\n",
1102 "/*u { /incompound true def } bd\n",
1103 "/*U { /incompound false def f} bd\n",
1104 "/k { setcmykcolor } bd\n",
1114 # Print program version {{{
1115 print("$progname v$VERSION\n");
1120 # Send the help message to stdout {{{
1123 if ($Opt{'verbose
'}) {
1129 Converts between various GPS formats.
1131 Usage: $progname [options] [file [files [...]]]
1132 $progname -S [file [files [...]]]
1133 $progname -u [file [files [...]]]
1138 Check for broken chronology, warn about entries with an old
1141 Skip duplicated coordinates.
1143 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1145 Comment out entries which is obviously wrong. Use together with
1146 --chronology to fix those kind of errors. Does not work with GPX
1149 Used by the pgwupd format. Specifies from which date waypoints
1150 should be updated. No checks for valid date format here, let
1151 PostgreSQL take care of that. All variants it understands can be
1156 Print only trackpoints inside a rectangle specified by --pos1 and
1159 Use x as undefined value. Default: "$Udef".
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 it
1242 under the terms of the GNU General Public License as published by the
1243 Free Software Foundation
, either version
3 of the License
, or (at your
1244 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 along
1253 If
not, see L
<http
://www
.gnu
.org
/licenses/>.
1256 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :