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,
67 $progname =~ s/^.*\/(.*?)$/$1/;
70 my $id_date = $rcs_id;
71 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
73 push(@main::version_array
, $rcs_id);
75 Getopt
::Long
::Configure
("bundling");
77 # Command line options {{{
79 "chronology" => \
$Opt{'chronology'},
80 "create-breaks|t" => \
$Opt{'create-breaks'},
81 "debug" => \
$Opt{'debug'},
82 "double-y-scale|y" => \
$Opt{'double-y-scale'},
83 "epoch|e" => \
$Opt{'epoch'},
84 "fix" => \
$Opt{'fix'},
85 "from-date=s" => \
$Opt{'from-date'},
86 "help|h" => \
$Opt{'help'},
87 "inside" => \
$Opt{'inside'},
88 "near" => \
$Opt{'near'},
89 "output-format|o=s" => \
$Opt{'output-format'},
90 "outside" => \
$Opt{'outside'},
91 "pos1=s" => \
$Opt{'pos1'},
92 "pos2=s" => \
$Opt{'pos2'},
93 "require|r=s" => \
$Opt{'require'},
94 "round|R=s" => \
$Opt{'round'},
95 "save-to-file|S=s" => \
$Opt{'save-to-file'},
96 "short-date|s" => \
$Opt{'short-date'},
97 "skip-dups|d" => \
$Opt{'skip-dups'},
98 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
99 "undefined|n=s" => \
$Opt{'undefined'},
100 "verbose|v+" => \
$Opt{'verbose'},
101 "version" => \
$Opt{'version'},
104 ) || die("$progname: Option error. Use -h for help.\n");
108 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
110 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
111 $GPST::Spc
= $Opt{'strip-whitespace'} ?
"" : " ";
112 my $Spc = $GPST::Spc
; # FIXME
115 my ($last_lon, $last_lat, $last_line) =
116 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
117 my ($lat1, $lon1, $lat2, $lon2) =
118 (-1000, -1000, 1000, 1000);
121 'gpsbabel' => '/usr/local/bin/gpsbabel',
126 if ($Opt{'output-format'} eq "pgtab") {
127 $Opt{'require'} .= "p";
130 'ele' => ($Opt{'require'} =~ /e/) ?
1 : 0,
131 'position' => ($Opt{'require'} =~ /p/) ?
1 : 0,
132 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0,
134 $Opt{'require'} =~ /[^ept]/
135 && die("$0: Unknown flag in --require (-r) value\n");
137 $Opt{'debug'} && ($Debug = 1);
138 $Opt{'help'} && usage
(0);
139 if ($Opt{'version'}) {
144 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
148 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
163 if ($Opt{'epoch'} && $Opt{'short-date'}) {
164 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
167 if ($Opt{'inside'} && $Opt{'outside'}) {
168 die("$progname: Cannot mix the --inside and --outside options\n");
171 # To avoid printing out extra "/> at the start of svg output:
172 my $svg_start_thing = "";
176 if (defined($Opt{'round'})) {
177 my $R = $Opt{'round'};
178 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
181 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
183 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
190 my $from_stdin = scalar(@ARGV) ?
0 : 1;
192 $from_stdin && push(@ARGV, "-");
194 for $curr_file (@ARGV) {
195 # Scan through stdin or specified files and send every GPS entry to
198 print(STDERR
"$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
199 if (open(CurrFP
, "<$curr_file")) {
204 'year' => '', 'month' => '', 'day' => '',
205 'hour' => '', 'min' => '', 'sec' => '',
208 'lat' => '', 'lon' => '',
215 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
216 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
218 if ($Opt{'save-to-file'} ne "\n") {
219 push(@first_lines, $_);
221 s/^# error // && ($Dat{'error'} = "error");
222 s/^# ?// && ($Dat{'error'} = "desc");
224 if (m
#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
225 # gpsml — The main storage format {{{
226 my ($Elem, $Props, $Data) =
228 my $err_str = ($Props =~ /\berr="(.*?)"/) ?
$1 : "error";
229 $Elem eq "etp" && ($Dat{'error'} = $err_str);
231 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
233 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T
](\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
235 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
236 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
241 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
242 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
243 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
244 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
247 } elsif (m
#^<break\b.*?/>#) {
249 } elsif (m
#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
253 } elsif (m
#^<desc\b.*?>(.*$)#s) {
254 $Dat{'type'} = "desc";
256 until ($Txt =~ m
#</desc>#s) {
259 $Txt =~ s
#^(.*)(</desc>.*$)#$1#s;
264 $xml_data .= join("", <CurrFP
>);
265 if (!length($Opt{'output-format'})) {
266 $Opt{'output-format'} = "gpx";
267 print_header
(*STDOUT
);
269 read_xmlfile
($xml_data);
273 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
274 # CSV format, epoch style {{{
275 my ($ep_time, $lon_val, $lat_val, $Alt) =
277 $Dat{'epoch'} = $ep_time;
278 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
279 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
280 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
281 $Dat{'month'}++; # Urgh Ⅰ
282 $Dat{'year'} += 1900; # Urgh Ⅱ
287 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)[T\
](\d\d
):?
(\d\d
):?
(\d\d
)Z?
\t
288 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
291 # CSV format, human-readable date format {{{
292 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
293 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
294 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
300 } elsif (/^Trackpoint\t/) {
301 # 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 {{{
304 # N60.41630 E5.31675\t
305 # 09.02.2006 20:24:37 (UTC)\t
313 $Orig =~ s/[\r\n]+$//;
314 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
315 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
317 # Nødløsning for å unngå at variabler
319 "\t\t\t\t\t\t\t\t\t\t"
322 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
323 "Time_f=\"$Time_f\"\n",
324 "Alt_f=\"$Alt_f\"\n",
325 "Depth_f=\"$Depth_f\"\n",
326 "Leglength_f=\"$Leglength_f\"\n",
327 "Legtime_f=\"$Legtime_f\"\n",
328 "Legspeed_f=\"$Legspeed_f\"\n",
329 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
334 $Legtime_hour, $Legtime_min, $Legtime_sec,
335 $Legspeed, $Legspeed_unit,
337 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
338 "", "", "", "", "", "", "", "", "", "");
339 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
340 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
341 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
342 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
343 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
344 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
345 ($Dat{'ele'} = $1, $Alt_unit = $2);
346 D
("ele = \"$Dat{'ele'}\"");
347 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
348 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
349 # MapSource in win xp writes YYYY, but YY in win98se.
351 defined($Dat{'year'})
352 && $Dat{'year'} =~ /\d/
353 && $Dat{'year'} < 1900
354 ) && ($Dat{'year'} += 2000);
357 } elsif (/^Track\t(.*?)\t/) {
358 $Dat{'title'} = txt_to_xml
($1);
359 $Dat{'type'} = "title";
365 (\d\d
)/(\d\d)/(\d\d\d\d
)\
(\d\d
):(\d\d
):(\d\d
)\t
370 # T 09/01/2002 11:51:26 60°23'36.3" 5°
19'35.9" {{{
371 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
372 ($Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
373 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'},
374 $lat_d, $lat_m, $lat_s,
375 $lon_d, $lon_m, $lon_s) =
380 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
381 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
382 $Dat{'lat
'} = sprintf("%${Flat}f",
383 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
384 $Dat{'lon
'} = sprintf("%${Flon}f",
385 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
390 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
391 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
394 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
395 ($Dat{'lat
'}, $Dat{'lon
'}, $Dat{'speed
'},
397 $Dat{'month
'}, $Dat{'day
'}, $Dat{'year
'},
398 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
406 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
416 (\d\d) # Latitude degree
417 (\d\d) # Latitude minute
418 (\d\d\d) # Latitude minute decimals
420 (\d\d\d) # Longitude degree
421 (\d\d) # Longitude minute
422 (\d\d\d) # Longitude minute degree
428 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
429 $lon_degmin, $lon_mindec);
430 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'}, $Dat{'hour
'},
431 $Dat{'min
'}, $Dat{'sec
'}, $NS, $lat_deg,
432 $lat_degmin, $lat_mindec, $EW,
433 $lon_deg, $lon_degmin, $lon_mindec,
434 $Dat{'accur
'}, $Dat{'ele
'}, $Dat{'unknown
'}) =
435 ($2+2000, $3, $4, $5,
440 my $ep_time = timegm_nocheck(
441 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
442 $Dat{'day
'}, $Dat{'month
'}-1, $Dat{'year
'}
444 $last_time = $ep_time;
445 my $Flat = defined($Round{'lat
'}) ? ".$Round{'lat
'}" : "";
446 my $Flon = defined($Round{'lon
'}) ? ".$Round{'lon
'}" : "";
447 my $tmp_lon = sprintf(
452 my $tmp_lat = sprintf("%${Flat}f",
456 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
457 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
458 $Dat{'lat
'} = $tmp_lat;
459 $Dat{'lon
'} = $tmp_lon;
462 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
463 # @020721221336__________________________________________ {{{
464 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
465 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}, $Dat{'rest
'}) =
468 $Dat{'error
'} = "nosignal";
471 } elsif (/^xmaplog /) {
474 ($Opt{'output
-format
'} eq "csv") && ($Dat{'break'} = 1);
475 } elsif (/^Pause: /) {
476 # NOP, is here to cope with old files I’ve lying around.
477 } elsif ($Dat{'error
'} eq "desc") {
479 if (defined($Comment)) {
480 $Comment =~ s/^\s*(.*?)\s*$/$1/;
481 if ($Opt{'output
-format
'} eq "gpsml") {
482 $Dat{'desc
'} = txt_to_xml($Comment);
483 $Dat{'type
'} = "desc";
488 $Opt{'verbose
'} && warn("Line $.: Unknown: \"$_\"\n");
493 warn("$progname: $curr_file: Cannot open file for read: $!\n");
498 print_footer(*STDOUT);
504 my $Txt = join("", @_);
505 $Txt =~ s/<!--(.*?)-->//gs;
506 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
514 # D("print_xml_gps(\"$Orig\")\n");
515 $Str =~ s/<!--(.*?)-->//gs;
516 my $fromdate_str = "";
517 if ($Opt{'from
-date
'}) {
518 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
520 if ($Opt{'output
-format
'} =~ /^(pgwtab|pgwupd)$/) {
524 <wpt\b(.*?)>(.*?)</wpt>
529 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
530 ('\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
', '\N
');
532 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
533 ($Lat = postgresql_copy_safe($1));
534 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
535 ($Lon = postgresql_copy_safe($1));
536 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
537 ($Name = postgresql_copy_safe(xml_to_txt($2)));
538 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
539 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
540 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
541 ($Type = postgresql_copy_safe(xml_to_txt($2)));
542 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
543 ($Time = postgresql_copy_safe(xml_to_txt($2)));
544 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
545 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
546 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
547 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
548 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
549 ($Src = postgresql_copy_safe(xml_to_txt($2)));
550 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
551 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
553 if ($Opt{'output
-format
'} eq "pgwtab") {
567 } elsif ($Opt{'output
-format
'} eq "pgwupd") {
571 " UPDATE logg SET sted = clname(coor) " .
572 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
573 " UPDATE logg SET dist = cldist(coor) " .
574 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
585 <trk\b(.*?)>(.*?)</trk>
592 <name\b(.*?)>(.*?)</name>
595 $tmp_dat{'title
'} = $2;
596 $tmp_dat{'type
'} = "title";
597 $tmp_dat{'error
'} = "";
598 print_entry(%tmp_dat);
603 <trkseg\b(.*?)>(.*?)</trkseg>
609 <trkpt\b(.*?)>(.*?)</trkpt>
612 my ($attr_trkpt, $el_trkpt) =
615 'year
' => '', 'month
' => '', 'day
' => '',
616 'hour
' => '', 'min
' => '', 'sec
' => '',
619 'lat
' => '', 'lon
' => '',
625 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon
'} = $1);
626 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat
'} = $1);
627 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele
'} = $1);
631 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
632 (\d\d):?(\d\d):?([\d\.]+)Z</time>
635 ($Dat{'year
'}, $Dat{'month
'}, $Dat{'day
'},
636 $Dat{'hour
'}, $Dat{'min
'}, $Dat{'sec
'}) =
637 ($1, $2, $3, $4, $5, $6);
653 local *OutFP = shift;
654 if ($Opt{'output
-format
'} eq "gpsml") {
656 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
660 } elsif ($Opt{'output
-format
'} eq "gpstrans") {
661 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
662 "Datum[100]: WGS 84\n");
663 } elsif ($Opt{'output
-format
'} eq "gpx") {
665 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
667 qq{$Spc${Spc}version="1.1"\n},
668 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
669 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
670 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
671 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
672 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
675 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
677 } elsif ($Opt{'output
-format
'} eq "ps") {
678 print(OutFP ps_header(532, 6034, 533, 6040));
680 } elsif ($Opt{'output
-format
'} eq "svg") {
682 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
683 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
684 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
685 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
686 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
687 "$Spc$Spc<title></title>\n",
688 "$Spc$Spc<desc></desc>\n",
696 local *OutFP = shift;
697 if ($Opt{'output
-format
'} eq "gpsml") {
702 } elsif ($Opt{'output
-format
'} eq "gpx") {
704 "$Spc$Spc$Spc$Spc</trkseg>\n",
708 } elsif ($Opt{'output
-format
'} eq "poscount") {
709 while (my ($l_name, $l_val) = each %Poscount) {
710 $l_name =~ /^(.+?),(.+?)$/
711 && print(OutFP "$1\t$2\t$l_val\n");
713 } elsif ($Opt{'output
-format
'} eq "ps") {
719 } elsif ($Opt{'output
-format
'} eq "svg") {
720 print(OutFP "\"/>\n</svg>\n");
726 # Print a GPS entry with time, latitude, longitude and elevation in
730 defined($Dat{'desc
'}) || ($Dat{'desc
'} = "");
731 defined($Dat{'ele
'}) || ($Dat{'ele
'} = "");
732 defined($Dat{'lat
'}) || ($Dat{'lat
'} = "");
733 defined($Dat{'lon
'}) || ($Dat{'lon
'} = "");
734 defined($Dat{'year
'}) || ($Dat{'year
'} = "");
735 my $print_time = length($Dat{'year
'}) ? 1 : 0;
736 my $print_pos = (length($Dat{'lat
'}) && length($Dat{'lon
'})) ? 1 : 0;
738 $Dat{'lat
'} = $Dat{'lon
'} = "";
740 my $print_ele = length($Dat{'ele
'}) ? 1 : 0;
741 my $print_desc = length($Dat{'desc
'}) ? 1 : 0;
743 D("print_entry(\"" . join("\", \"", @_) . "\");");
746 if ($Opt{'near
'} && $print_pos) {
747 $Line .= sprintf("%s ",
748 list_nearest_waypoints($Dat{'lat
'}, $Dat{'lon
'}));
751 if (length($Opt{'round
'})) {
752 for my $Tmp (qw{ lat lon ele }) {
753 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
755 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
760 if ($Opt{'output
-format
'} eq "poscount") {
761 if (!length($Dat{'error
'})) {
762 my $Name = "$Dat{'lon
'},$Dat{'lat
'}";
763 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
770 $ep_time = timegm_nocheck(
771 $Dat{'sec
'}, $Dat{'min
'}, $Dat{'hour
'},
772 $Dat{'day
'}, $Dat{'month
'} - 1, $Dat{'year
'}
774 $Dat{'epoch
'} = $ep_time;
775 $Dat{'year
'} = sprintf("%04u", $Dat{'year
'});
776 $Dat{'month
'} = sprintf("%02u", $Dat{'month
'});
777 $Dat{'day
'} = sprintf("%02u", $Dat{'day
'});
778 $Dat{'hour
'} = sprintf("%02u", $Dat{'hour
'});
779 $Dat{'min
'} = sprintf("%02u", $Dat{'min
'});
780 $Dat{'sec
'} = sprintf("%02u", $Dat{'sec
'});
781 if ($Opt{'chronology
'}) {
782 if ($last_time > $ep_time && !length($Dat{'error
'})) {
784 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
785 $progname, sec_to_string($last_time, "T"),
786 sec_to_readable($last_time-$ep_time),
787 sec_to_string($ep_time, "T")
789 # FIXME: Make --fix work with gpx.
790 if ($Opt{'fix
'} && ($Opt{'output
-format
'} !~ /^gpx$/)) {
791 $Dat{'error
'} = "chrono";
794 } elsif ($last_time == $ep_time && !length($Dat{'error
'})) {
796 "%s: $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(ToFP, ">", $file_name)) {
844 ) || die("$progname: $file_name: Cannot write to file: $!\n");
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{'type
'} 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'");
916 if ($Opt{'output
-format
'} eq "gpsml") {
917 $Line .= sprintf("<pause>%s</pause>\n",
918 sec_to_readable($ep_time-$last_time));
919 } elsif ($Opt{'output
-format
'} eq "clean") {
920 $pause_len && ($Line .= "\n");
921 } elsif ($Opt{'output
-format
'} eq "csv") {
922 $Line .= sprintf("# Pause: %s\n# move\n",
923 sec_to_readable($ep_time-$last_time));
924 } elsif ($Opt{'output
-format
'} eq "xgraph") {
925 $pause_len && ($Line .= "move ");
932 # Valid data was found, send to stdout {{{
933 unless ($first_time) {
934 $first_time = $ep_time;
936 if ($Opt{'double
-y
-scale
'} && length($Dat{'lat
'})) {
939 if ($Opt{'output
-format
'} eq "gpsml") {
940 if ($Dat{'type
'} eq "tp") {
941 $Dat{'format
'} = "gpsml";
942 $Line .= trackpoint(%Dat);
943 } elsif ($Dat{'type
'} =~ /^(pause|desc|title)$/) {
944 $Line .= sprintf("<%s>%s</%s>\n",
949 } elsif ($Opt{'output
-format
'} eq "pgtab") {
950 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
951 $Dat{'format
'} = "pgtab";
952 $Line .= trackpoint(%Dat);
954 } elsif ($Opt{'output
-format
'} eq "xgraph") {
955 if ($print_pos && !length($Dat{'error
'})) {
956 $Dat{'format
'} = "xgraph";
957 $Line .= trackpoint(%Dat);
959 } elsif($Opt{'output
-format
'} eq "gpstrans") {
960 if ($print_pos && !length($Dat{'error
'})) {
961 $Dat{'format
'} = "gpstrans";
962 $Line .= trackpoint(%Dat);
964 } elsif($Opt{'output
-format
'} eq "gpx") {
965 if ($Dat{'type
'} eq "tp") {
966 $Dat{'format
'} = "gpx";
967 $Line .= trackpoint(%Dat);
969 } elsif ($Opt{'output
-format
'} eq "clean") {
970 if ($Dat{'type
'} eq "tp" && !length($Dat{'error
'})) {
971 $Dat{'format
'} = "clean";
972 $Line .= trackpoint(%Dat);
974 } elsif ($Opt{'output
-format
'} eq "ps") {
977 ? "f\n$Dat{'lon
'} $Dat{'lat
'} m\n"
978 : "$Dat{'lon
'} $Dat{'lat
'} l\n"
980 } elsif ($Opt{'output
-format
'} eq "svg") {
982 ($last_lon == 1000) || $pause_len
984 "$svg_start_thing<path\n",
985 " stroke=\"blue\"\n",
986 " stroke-width=\"0.001\"\n",
989 "M $Dat{'lon
'} $Dat{'lat
'}\n")
990 : "L $Dat{'lon
'} $Dat{'lat
'}\n"
992 } elsif ($Opt{'output
-format
'} eq "ygraph") {
993 if (!length($Dat{'error
'})) {
994 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
995 $Line .= "\"Time = $Time.0\n$Dat{'lon
'} $Dat{'lat
'}\n\n";
997 } elsif ($Opt{'output
-format
'} eq "csv") {
999 if (!length($Dat{'error
'})) {
1000 $Dat{'format
'} = "csv";
1005 : $Opt{'short
-date
'}
1006 ? "$Dat{'year
'}$Dat{'month
'}$Dat{'day
'}T" .
1007 "$Dat{'hour
'}$Dat{'min
'}$Dat{'sec
'}Z"
1008 : "$Dat{'year
'}-$Dat{'month
'}-$Dat{'day
'}T" .
1009 "$Dat{'hour
'}:$Dat{'min
'}:$Dat{'sec
'}Z"
1013 $print_ele ? $Dat{'ele
'} : "", # Elevation
1018 } elsif ($Opt{'output
-format
'} eq "pgwtab") {
1019 # FIXME: NOP at the moment.
1021 die("$progname: \"$Opt{'output
-format
'}\": " .
1022 "Unknown output format\n");
1027 if (!$last_time && $Opt{'output
-format
'} eq "ps") {
1028 $Line .= "$Dat{'lon
'} $Dat{'lat
'} m\n";
1032 if ($Dat{'break'}) {
1033 if ($Opt{'output
-format
'} eq "gpsml") {
1034 $Line = "<break/>\n$Line";
1036 (!$pause_len && ($Opt{'output
-format
'} eq "xgraph"))
1037 && ($Line .= "move $Line");
1038 ($Opt{'output
-format
'} eq "clean") && ($Line .= "\n");
1039 if ($Opt{'output
-format
'} eq "gpx") {
1040 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1041 "$Spc$Spc$Spc$Spc<trkseg>\n";
1047 $print_time && ($last_time = $ep_time);
1049 $last_lon = $Dat{'lon
'};
1050 $last_lat = $Dat{'lat
'};
1052 $last_line = $data_line;
1053 $svg_start_thing = "\"/>\n";
1058 # Send a Postscript header to stdout {{{
1059 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1060 my $Date = sec_to_string(time);
1062 "%!PS-Adobe-3.0 EPSF-3.0\n",
1063 "%%Creator: $rcs_id\n",
1065 "%%CreationDate: $Date\n",
1066 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1067 "%%DocumentData: Clean7Bit\n",
1070 "/bd { bind def } bind def\n",
1071 "/incompound false def\n",
1072 "/m { moveto } bd\n",
1073 "/l { lineto } bd\n",
1074 "/c { curveto } bd\n",
1075 "/F { incompound not {fill} if } bd\n",
1076 "/f { closepath F } bd\n",
1077 "/S { stroke } bd\n",
1078 "/*u { /incompound true def } bd\n",
1079 "/*U { /incompound false def f} bd\n",
1080 "/k { setcmykcolor } bd\n",
1090 # Print program version {{{
1091 for (@main::version_array) {
1098 # Send the help message to stdout {{{
1101 if ($Opt{'verbose
'}) {
1107 Converts between various GPS formats.
1109 Usage: $progname [options] [file [files [...]]]
1110 $progname -S [file [files [...]]]
1111 $progname -u [file [files [...]]]
1116 Check for broken chronology, warn about entries with an old
1119 Skip duplicated coordinates.
1121 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1123 Comment out entries which is obviously wrong. Use together with
1124 --chronology to fix those kind of errors. Does not work with GPX
1127 Used by the pgwupd format. Specifies from which date waypoints
1128 should be updated. No checks for valid date format here, let
1129 PostgreSQL take care of that. All variants it understands can be
1134 Print only trackpoints inside a rectangle specified by --pos1 and
1137 Use x as undefined value. Default: "$Udef".
1139 Add names of the three closest waypoints to the trackpoint.
1140 Unfinished and experimental, needs gpsbabel, which is called from
1141 the program as "$Cmd{'gpsbabel
'}".
1142 -o, --output-format x
1143 Use output format x:
1158 Print only trackpoints outside a rectangle specified by --pos1 and
1162 Specifies one corner where x is in "lat,lon" format (decimal
1163 degrees, negative for west or south) of area rectangle used by the
1164 --inside and --outside options.
1166 Specify requirements for trackpoints to be written. x is a string
1167 with the following flags:
1169 Print only waypoints which have an elevation.
1171 Print only waypoints which have a position.
1173 Print only waypoints which have a timestamp.
1174 -R, --round x=y[,x2=y2[...]]
1175 Round trackpoint element x to y decimals. Example:
1176 --round lat=4,lon=5,ele=1
1178 Use short date format.
1179 -S, --save-to-file x
1180 Save the unconverted data to a file with a filename starting with
1181 the timestamp of the first trackpoint. The parameter string x is
1182 added at the end of the filename. For the time being this option
1183 will ignore all other options. Note: If several files are specified
1184 on the command line, all data will be saved into only one file. This
1185 behaviour may change in the future.
1187 Create breaks in track between points with a difference more than
1188 $PAUSE_LIMIT seconds.
1190 Increase level of verbosity. Can be repeated.
1192 Print version information.
1193 -w, --strip-whitespace
1194 Strip all unnecessary whitespace.
1195 -y, --double-y-scale
1196 Double Y scale (latitude) to get it right in gnuplot.
1198 Print debugging messages.
1206 # Print a status message to stderr based on verbosity level {{{
1207 my ($verbose_level, $Txt) = @_;
1209 if ($Opt{'verbose'} >= $verbose_level) {
1210 print(STDERR
"$progname: $Txt\n");
1218 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1220 # This program is free software; you can redistribute it and/or modify
1221 # it under the terms of the GNU General Public License as published by
1222 # the Free Software Foundation; either version 2 of the License, or (at
1223 # your option) any later version.
1225 # This program is distributed in the hope that it will be useful, but
1226 # WITHOUT ANY WARRANTY; without even the implied warranty of
1227 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1228 # See the GNU General Public License for more details.
1230 # You should have received a copy of the GNU General Public License
1231 # along with this program; if not, write to the Free Software
1232 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1236 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :