3 #=======================================================================
5 # Converts between various GPS formats
8 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
9 # License: GNU General Public License, see end of file for legal stuff.
10 #=======================================================================
14 use Time
::Local qw
{ timegm_nocheck
};
21 # Initial values for command line arguments {{{
23 'comment-out-dups' => 0,
26 'double-y-scale' => 0,
32 'output-format' => "gpsml",
36 'print-comments' => 0,
38 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
41 'strip-whitespace' => 0,
49 $progname =~ s
#^.*/(.*?)$#$1#;
52 my $id_date = $rcs_id;
53 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
55 Getopt
::Long
::Configure
("bundling");
57 # Command line options {{{
58 "chronology" => \
$Opt{'chronology'},
59 "comment-out-dups|u" => \
$Opt{'comment-out-dups'},
60 "create-breaks|t" => \
$Opt{'create-breaks'},
61 "debug" => \
$Opt{'debug'},
62 "double-y-scale|y" => \
$Opt{'double-y-scale'},
63 "epoch|e" => \
$Opt{'epoch'},
64 "fix" => \
$Opt{'fix'},
65 "help|h" => \
$Opt{'help'},
66 "inside" => \
$Opt{'inside'},
67 "near" => \
$Opt{'near'},
68 "output-format|o=s" => \
$Opt{'output-format'},
69 "outside" => \
$Opt{'outside'},
70 "pos1=s" => \
$Opt{'pos1'},
71 "pos2=s" => \
$Opt{'pos2'},
72 "print-comments|C" => \
$Opt{'print-comments'},
73 "require|r=s" => \
$Opt{'require'},
74 "save-to-file|S=s" => \
$Opt{'save-to-file'},
75 "short-date|s" => \
$Opt{'short-date'},
76 "skip-dups|d" => \
$Opt{'skip-dups'},
77 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
78 "undefined|n=s" => \
$Opt{'undefined'},
79 "use-comma|c" => \
$Opt{'use-comma'},
80 "verbose|v" => \
$Opt{'verbose'},
81 "version" => \
$Opt{'version'},
83 ) || die("$progname: Option error. Use -h for help.\n");
87 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
88 my $Des = $Opt{'use-comma'} ?
"," : ".";
90 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
91 my $Spc = $Opt{'strip-whitespace'} ?
"" : " ";
92 my $in_dupskip = 0; # Er 1 hvis vi holder på med ignorering av duplikater
93 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
96 my ($last_lon, $last_lat, $last_ele, $last_line) =
97 ( 1000, 1000, 100000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
98 my ($lat1, $lon1, $lat2, $lon2) =
99 (-1000, -1000, 1000, 1000);
100 my ($start_date, $end_date, $found_dup) =
107 'altitude' => ($Opt{'require'} =~ /a/) ?
1 : 0,
108 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0
110 $Opt{'require'} =~ /[^at]/ && die("$0: Unknown flag in --require (-r) value\n");
112 $Opt{'debug'} && ($Debug = 1);
113 $Opt{'help'} && usage
(0);
114 $Opt{'version'} && print_version
();
116 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
120 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
135 if ($Opt{'inside'} && $Opt{'outside'}) {
136 die("$progname: Cannot mix the --inside and --outside options\n");
139 my $waypoint_file = "/home/sunny/gps/waypoints.gpx";
141 # To avoid printing out extra "/> at the start of svg output:
142 my $svg_start_thing = "";
144 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
145 # Kunne vært et eget script på grunn av at det gjør sine helt egne
146 # greier, men like greit å samle det på en plass.
147 # FIXME: Fjerner ikke første duplikatentryen.
148 # FIXME: Se om det går å få flytta den inn i print_entry() så man
149 # slipper å ha to gptrans_conv’er i pipen.
150 # FIXME: Legg inn alle formatene.
151 if (0 && $Opt{'comment-out-dups'}) {
152 # Comment out areas without reception {{{
154 if (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
156 my ($lat_val, $lon_val, $Speed, $Unkn, $Month, $Day, $Year, $Hour, $Min, $Sec) =
157 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
168 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
172 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n");
179 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
186 my $from_stdin = scalar(@ARGV) ?
0 : 1;
188 $from_stdin && push(@ARGV, "-");
190 for $curr_file (@ARGV) {
191 # Scan through stdin or specified files and send every GPS entry to
194 D
("Opening \"$curr_file\" for read");
195 if (open(CurrFP
, "<$curr_file")) {
200 'year' => '', 'month' => '', 'day' => '',
201 'hour' => '', 'min' => '', 'sec' => '',
202 'lat' => '', 'lon' => '',
209 if ($Opt{'save-to-file'} ne "\n") {
210 push(@first_lines, $_);
211 $_ =~ s/^# ?//; # Also read commented-out lines.
214 if (m
#^<(e?tp)\b.*?>(.*?)</(e?tp)>$#) {
215 # gpsml — The main storage format {{{
218 $Elem eq "etp" && ($Dat{'error'} = 1);
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'} = xml_to_txt($1));
236 } elsif (m
#^<break\b.*?/>#) {
238 } elsif (m
#^<(desc|title|pause)\b.*?>(.*?)</(desc|title|pause)>#) {
240 $Dat{$1} = xml_to_txt
($2);
242 } elsif (/^<gpx\b/) {
244 $xml_data .= join("", <CurrFP
>);
245 if (!length($Opt{'output-format'})) {
246 $Opt{'output-format'} = "gpx";
247 print_header
(*STDOUT
);
249 read_xmlfile
($xml_data);
251 } elsif (/^# Pause: /) {
252 $Opt{'print-comments'} && print;
253 } elsif (/^# move$/) {
256 $Opt{'print-comments'} && print;
257 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
258 # CSV format, epoch style {{{
259 my ($ep_time, $lon_val, $lat_val, $Alt) =
261 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
262 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
263 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
264 $Dat{'month'}++; # Urgh Ⅰ
265 $Dat{'year'} += 1900; # Urgh Ⅱ
268 } elsif (m
#^(\d\d\d\d)-?(\d\d)-?(\d\d)[T ](\d\d):?(\d\d):?(\d\d)Z?\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
269 # CSV format, human-readable date format {{{
270 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
271 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
272 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
278 } elsif (/^Trackpoint\t/) {
279 # 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 {{{
282 # N60.41630 E5.31675\t
283 # 09.02.2006 20:24:37 (UTC)\t
291 $Orig =~ s/[\r\n]+$//;
292 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
293 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
295 # Nødløsning for å unngå at variabler blir
297 "\t\t\t\t\t\t\t\t\t\t"
300 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
301 "Time_f=\"$Time_f\"\n",
302 "Alt_f=\"$Alt_f\"\n",
303 "Depth_f=\"$Depth_f\"\n",
304 "Leglength_f=\"$Leglength_f\"\n",
305 "Legtime_f=\"$Legtime_f\"\n",
306 "Legspeed_f=\"$Legspeed_f\"\n",
307 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
312 $Legtime_hour, $Legtime_min, $Legtime_sec,
313 $Legspeed, $Legspeed_unit,
315 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
316 "", "", "", "", "", "", "", "");
317 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
318 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
319 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
320 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
321 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
322 ($Alt_f =~ /^([\d+\.]+) (.*?)/) &&
323 ($Dat{'ele'} = $1, $Alt_unit = $2);
324 D
("ele = \"$Dat{'ele'}\"");
325 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
326 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
327 # MapSource in win xp writes YYYY, but YY in win98se.
328 (defined($Dat{'year'}) && $Dat{'year'} =~ /\d/ && $Dat{'year'} < 1900) && ($Dat{'year'} += 2000);
331 } elsif (m
#^T\t(\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)\t(.+)\xB0(.+)'(.+)"\t(.+)\xB0(.+)'(.+)"#) {
332 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
333 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
334 ($Dat{'month'}, $Dat{'day'}, $Dat{'year'},
335 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $lat_d,
336 $lat_m, $lat_s, $lon_d,
338 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
339 $Dat{'lat'} = sprintf("%.5f", 1*($lat_d+($lat_m/60)+($lat_s/3600)));
340 $Dat{'lon'} = sprintf("%.5f", $lon_d+($lon_m/60)+($lon_s/3600));
343 } elsif (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
344 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
345 ($Dat{'lat'}, $Dat{'lon'}, $Dat{'speed'},
347 $Dat{'month'}, $Dat{'day'}, $Dat{'year'},
348 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
349 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
353 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
363 (\d\d
) # Latitude degree
364 (\d\d
) # Latitude minute
365 (\d\d\d
) # Latitude minute decimals
367 (\d\d\d
) # Longitude degree
368 (\d\d
) # Longitude minute
369 (\d\d\d
) # Longitude minute degree
376 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
377 $lon_degmin, $lon_mindec);
378 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'}, $Dat{'hour'},
379 $Dat{'min'}, $Dat{'sec'}, $NS, $lat_deg,
380 $lat_degmin, $lat_mindec, $EW,
381 $lon_deg, $lon_degmin, $lon_mindec,
382 $Dat{'accur'}, $Dat{'ele'}, $Dat{'unknown'}) =
383 ($2+2000, $3, $4, $5, $6, $7, $8, $9, $10, $11,
384 $12, $13, $14, $15, $16, $17, $18);
385 my $ep_time = timegm_nocheck
($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'}, $Dat{'day'}, $Dat{'month'}-1, $Dat{'year'});
386 $last_time = $ep_time;
387 my $tmp_lon = sprintf("%.5f", $lon_deg + $lon_degmin/60 + $lon_mindec/60000);
388 my $tmp_lat = sprintf("%.5f", $lat_deg + $lat_degmin/60 + $lat_mindec/60000);
389 $tmp_lon =~ s/\./$Des/;
390 $tmp_lat =~ s/\./$Des/;
391 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
392 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
393 $Dat{'lat'} = $tmp_lat;
394 $Dat{'lon'} = $tmp_lon;
397 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(__________________________________________)/) {
398 # @020721221336__________________________________________ {{{
399 my ($Alfa, $Year, $Month, $Day, $Hour, $Min, $Sec, $Rest) =
400 ( $1, $2+2000, $3, $4, $5, $6, $7, $8);
401 $Opt{'output-format'} eq "csv" && print("\n");
404 } elsif (/^xmaplog /) {
405 ($Opt{'output-format'} eq "csv") && ($Opt{'save-to-file'} eq "\n") && print("\n");
407 ($Opt{'output-format'} eq "csv") && ($Opt{'save-to-file'} eq "\n") && print("\n");
409 if ($Opt{'print-comments'}) {
413 $Opt{'verbose'} && warn("Line $.: Unknown: \"$_\"\n");
418 warn("$progname: $curr_file: Cannot open file for read: $!\n");
423 print_footer
(*STDOUT
);
429 my $Txt = join("", @_);
430 # FIXME: The sequential stuff here is probably bad, but easy.
431 $Txt =~ s
#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
432 $Txt =~ s
#(<gps\b.*?>.*?</gps>)#print_xml_gps($1)#gse;
440 D
("print_xml_gps(\"$Orig\")\n");
443 <trk
\b(.*?
)>(.*?
)</trk
>
449 <trkseg
\b(.*?
)>(.*?
)</trkseg
>
455 <trkpt
\b(.*?
)>(.*?
)</trkpt
>
458 my ($attr_trkpt, $el_trkpt) =
460 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon'} = $1);
461 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat'} = $1);
462 ($el_trkpt =~ m
#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele'} = $1);
463 if ($el_trkpt =~ m
#<time>(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)\.?(\d*?)Z</time>#) {
464 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
465 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'secfrac'}) =
466 ($1, $2, $3, $4, $5, $6, $7);
482 D
("print_xml_gps(\"$Orig\")\n");
483 if ($Str =~ m
#<date>(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)\.?(\d*?)Z</date>#) {
484 ($Dat{'year'}, $Dat{'mon'}, $Dat{'day'}, $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'secfrac'}) =
485 ( $1, $2, $3, $4, $5, $6, $7);
487 if ($Str =~ m
#<pos>(.*?)</pos>#s) {
489 ($Txt =~ m
#<x\b.*?>(.*?)</x>#) && ($Dat{'lon'} = $1);
490 ($Txt =~ m
#<y\b.*?>(.*?)</y>#) && ($Dat{'lat'} = $1);
491 ($Txt =~ m
#<z\b.*?>(.*?)</z>#) && ($Dat{'ele'} = $1);
493 defined($Dat{'lon'}) || ($Dat{'lon'} = "");
494 defined($Dat{'lat'}) || ($Dat{'lat'} = "");
495 defined($Dat{'ele'}) || ($Dat{'ele'} = "");
502 local *OutFP
= shift;
503 if ($Opt{'output-format'} eq "gpsml") {
505 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
509 } elsif ($Opt{'output-format'} eq "gpstrans") {
510 print(OutFP
"Format: DMS UTC Offset: 0.00 hrs Datum[100]: WGS 84\n");
511 } elsif ($Opt{'output-format'} eq "gpx") {
513 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
516 "$Spc$Spc$Spc$Spc<trkseg>\n",
518 } elsif ($Opt{'output-format'} eq "ps") {
519 print(OutFP ps_header
(532, 6034, 533, 6040));
521 } elsif ($Opt{'output-format'} eq "xml") {
523 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
526 } elsif ($Opt{'output-format'} eq "svg") {
528 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
529 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
530 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
531 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
532 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
533 "$Spc$Spc<title></title>\n",
534 "$Spc$Spc<desc></desc>\n",
542 local *OutFP
= shift;
543 if ($Opt{'output-format'} eq "gpsml") {
548 } elsif ($Opt{'output-format'} eq "gpx") {
550 "$Spc$Spc$Spc$Spc</trkseg>\n",
554 } elsif ($Opt{'output-format'} eq "poscount") {
555 while (my ($l_name, $l_val) = each %Poscount) {
556 $l_name =~ /^(.+?),(.+?)$/ && print(OutFP
"$1\t$2\t$l_val\n");
558 } elsif ($Opt{'output-format'} eq "ps") {
564 } elsif ($Opt{'output-format'} eq "svg") {
565 print(OutFP
"\"/>\n</svg>\n");
566 } elsif ($Opt{'output-format'} eq "xml") {
567 print(OutFP
"</gpslog>\n");
573 # Print a GPS entry with time, latitude, longitude and altitude in
577 my $print_time = length($Dat{'year'}) ?
1 : 0;
578 my $print_ele = length($Dat{'ele'}) ?
1 : 0;
579 my $print_desc = length($Dat{'desc'}) ?
1 : 0;
581 D
("print_entry(\"" . join("\", \"", @_) . "\");");
585 $Line .= sprintf("%s ", list_nearest_waypoints
($Dat{'lat'}, $Dat{'lon'}));
588 if ($Opt{'output-format'} eq "poscount") {
589 my ($Lat_str, $Lon_str) =
591 $Dat{'lon'} =~ /^(\d+\.\d\d)/ && ($Lon_str = $1);
592 $Dat{'lat'} =~ /^(\d+\.\d\d)/ && ($Lat_str = $1);
593 my $Name = "${Lon_str},${Lat_str}";
594 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
600 $ep_time = timegm_nocheck
($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'}, $Dat{'day'}, $Dat{'month'} - 1, $Dat{'year'});
601 $Dat{'year'} = sprintf("%04u", $Dat{'year'});
602 $Dat{'month'} = sprintf("%02u", $Dat{'month'});
603 $Dat{'day'} = sprintf("%02u", $Dat{'day'});
604 $Dat{'hour'} = sprintf("%02u", $Dat{'hour'});
605 $Dat{'min'} = sprintf("%02u", $Dat{'min'});
606 $Dat{'sec'} = sprintf("%02u", $Dat{'sec'});
607 if ($Opt{'chronology'}) {
608 if ($last_time > $ep_time) {
610 "%s: \"%sZ\": Next date is %s in the past (%sZ)\n",
611 $progname, sec_to_string
($last_time, "T"),
612 sec_to_readable
($last_time-$ep_time),
613 sec_to_string
($ep_time, "T")
615 # FIXME: Make --fix work with gpx and xml.
616 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^(gpx|xml)$/)) {
617 ($Line .= "# error ");
622 $Req{'time'} && return;
632 if ($Opt{'save-to-file'} ne "\n") {
634 my $base_name = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z$Opt{'save-to-file'}";
635 my $file_name = $base_name;
637 for (my $a = 1; (-e
$file_name) && ($a < 1000); $a++) {
638 $file_name = "$base_name.dup_$a";
641 die("$progname: $base_name: File already exists, and ran " .
642 "out of attempts to create unique file name\n");
644 if ($Opt{'verbose'}) {
645 warn("$progname: $base_name: File already exists, using " .
646 "unique name \"$file_name\" instead\n");
649 if (open(ToFP
, ">", $file_name)) {
651 print(ToFP
($from_stdin ?
@first_lines : ()), (length($xml_data) ?
$xml_data : <>)) ||
652 die("$progname: $file_name: Cannot write to file: $!\n");
655 if ($Opt{'output-format'} eq "gpsml") {
656 printf("<include>%s</include>\n",
657 txt_to_xml
($file_name));
658 } elsif ($Opt{'output-format'} eq "gpx") {
659 printf("<!-- Saved unconverted data to \"%s\" -->\n",
660 txt_to_xml
($file_name));
662 print("$progname: Saved unconverted data to \"$file_name\"\n");
666 die("$progname: $file_name: Cannot create file: $!\n");
673 ($Req{'altitude'} && !$print_ele) && return;
675 if ($Opt{'inside'} || $Opt{'outside'}) {
677 ($Dat{'lat'} < $lat1) ||
678 ($Dat{'lat'} > $lat2) ||
679 ($Dat{'lon'} < $lon1) ||
680 ($Dat{'lon'} > $lon2)
682 $Opt{'inside'} && return;
684 $Opt{'outside'} && return;
688 if ($Opt{'comment-out-dups'}) {
689 if (($Dat{'lat'} eq $last_lat) && ($Dat{'lon'} eq $last_lon)) {
690 unless ($found_dup) {
691 $start_date = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T$Dat{'hour'}$Dat{'min'}$Dat{'sec'}";
696 $end_date = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T$Dat{'hour'}$Dat{'min'}$Dat{'sec'}";
700 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
704 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n$_");
712 if ($Opt{'output-format'} eq "ps") {
716 # FIXME: $Opt{'skip_dups'} ble bytta ut med $Opt{'comment-out-dups'}
717 # i r583. Lurer på hva det gikk i der.
718 if ($Opt{'skip-dups'} && ($Dat{'lon'} eq $last_lon) && ($Dat{'lat'} eq $last_lat) && ($Dat{'ele'} eq $last_ele)) {
727 $in_dupskip && ($Line .= $last_line);
731 if ($Opt{'create-breaks'} && $ep_time-$last_time > $PAUSE_LIMIT && $last_time) {
732 $pause_len = $ep_time-$last_time;
733 D
("pause_len set to '$pause_len'");
737 if ($Opt{'output-format'} eq "gpsml") {
738 $Line .= sprintf("<pause>%s</pause>\n",
739 sec_to_readable
($ep_time-$last_time));
740 } elsif ($Opt{'output-format'} eq "csv") {
741 $Line .= sprintf("# Pause: %s\n# move\n",
742 sec_to_readable
($ep_time-$last_time));
747 # Valid data was found, send to stdout {{{
748 if ($Opt{'double-y-scale'}) {
751 if ($Opt{'output-format'} eq "gpsml") {
752 if ($Dat{'type'} eq "tp") {
753 my $Elem = $Dat{'error'} ?
"etp" : "tp";
757 ?
"<time>$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T" .
758 "$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z</time> "
760 (length($Dat{'lat'}))
761 ?
"<lat>" . $Dat{'lat'}*1.0 . "</lat> "
763 (length($Dat{'lon'}))
764 ?
"<lon>" . $Dat{'lon'}*1.0 . "</lon> "
767 ?
"<ele>" . $Dat{'ele'}*1.0 . "</ele> "
770 ?
sprintf("<desc>%s</desc> ",
771 txt_to_xml
($Dat{'desc'}))
775 } elsif ($Dat{'type'} =~ /^(pause|desc|title)$/) {
776 $Line .= sprintf("<%s>%s</%s>\n",
778 txt_to_xml
($Dat{$1}),
781 } elsif ($Opt{'output-format'} eq "xgraph") {
782 $pause_len && ($Line .= "move ");
783 ($Line .= "$Dat{'lon'} $Dat{'lat'}\n");
784 } elsif($Opt{'output-format'} eq "gpstrans") {
785 my ($gpt_lat, $gpt_lon) = (ddd_to_dms
($Dat{'lat'}), ddd_to_dms
($Dat{'lon'}));
787 $Line .= "T\t$Dat{'month'}/$Dat{'day'}/$Dat{'year'} $Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}\t$gpt_lat\t$gpt_lon\n";
789 $Line .= "T\t00/00/00 00:00:00\t$gpt_lat\t$gpt_lon\n";
791 } elsif($Opt{'output-format'} eq "gpx") {
793 "$Spc$Spc$Spc$Spc$Spc$Spc<trkpt lat=\"$Dat{'lat'}\" lon=\"$Dat{'lon'}\">$Spc",
795 ?
"<time>$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z</time>$Spc"
798 ?
"<ele>$Dat{'ele'}</ele>$Spc"
802 } elsif ($Opt{'output-format'} eq "clean") {
803 $pause_len && ($Line .= "\n");
804 ($Line .= "$Dat{'lon'}\t$Dat{'lat'}" . ($print_ele ?
"\t$Dat{'ele'}" : "") . "\n");
805 } elsif ($Opt{'output-format'} eq "ps") {
806 $Line .= ($pause_len ?
"f\n$Dat{'lon'} $Dat{'lat'} m\n" : "$Dat{'lon'} $Dat{'lat'} l\n");
807 } elsif ($Opt{'output-format'} eq "svg") {
809 ($last_lon == 1000) || $pause_len
811 "$svg_start_thing<path\n",
812 " stroke=\"blue\"\n",
813 " stroke-width=\"0.001\"\n",
816 "M $Dat{'lon'} $Dat{'lat'}\n",
818 : "L $Dat{'lon'} $Dat{'lat'}\n"
820 } elsif ($Opt{'output-format'} eq "xml") {
824 ?
"<date>$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z</date>$Spc"
827 (length($Dat{'lon'})) ?
"<x>$Dat{'lon'}</x>$Spc" : "",
828 (length($Dat{'lat'})) ?
"<y>$Dat{'lat'}</y>$Spc" : "",
829 ($print_ele) ?
"<z>$Dat{'ele'}</z>$Spc" : "",
833 } elsif ($Opt{'output-format'} eq "ygraph") {
834 my $Time = $print_time ?
($ep_time - $first_time) * 1 : 0;
835 $Line .= "\"Time = $Time.0\n$Dat{'lon'} $Dat{'lat'}\n\n";
836 } elsif ($Opt{'output-format'} eq "csv") {
838 $Dat{'lon'} =~ s/\./$Des/;
839 $Dat{'lat'} =~ s/\./$Des/;
840 # $do_print || print("skipping ");
846 ?
"$Dat{'year'}$Dat{'month'}$Dat{'day'}T$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z"
847 : "$Dat{'year'}-$Dat{'month'}-$Dat{'day'} $Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}"
851 $print_ele ?
$Dat{'ele'} : "", # Elevation
856 die("$progname: \"$Opt{'output-format'}\": Unknown output format\n");
861 if (!$last_time && $Opt{'output-format'} eq "ps") {
862 $Line .= "$Dat{'lon'} $Dat{'lat'} m\n";
867 if ($Opt{'output-format'} eq "gpsml") {
868 $Line = "<break/>\n$Line";
870 (!$pause_len && ($Opt{'output-format'} eq "xgraph")) && ($Line .= "move $Line");
871 ($Opt{'output-format'} eq "clean") && ($Line .= "\n");
872 if ($Opt{'output-format'} eq "gpx") {
873 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n$Spc$Spc$Spc$Spc<trkseg>\n";
879 $last_time = $ep_time;
880 $last_lon = $Dat{'lon'};
881 $last_lat = $Dat{'lat'};
882 $last_ele = $Dat{'ele'};
883 $last_line = $data_line;
884 $svg_start_thing = "\"/>\n";
888 sub list_nearest_waypoints
{
890 my ($Lat, $Lon, $Count) = @_;
891 # FIXME: Incredible unfinished and kludgy.
892 if (open(WaypFP
, "gpsbabel -i gpx -f $waypoint_file -x radius,lat=$Lat,lon=$Lon,distance=1000 -o gpx -F - |")) {
893 my $Str = join("", <WaypFP
>);
895 ^.*?
<wpt\s
.*?
>.*?
<name
>(.+?
)</name>.*?</wpt
>.*?
896 .*?
<wpt\s
.*?
>.*?
<name
>(.+?
)</name>.*?</wpt
>.*?
897 .*?
<wpt\s
.*?
>.*?
<name
>(.+?
)</name>.*?</wpt
>.*$
903 die("$progname: Cannot open gpsbabel pipe: $!\n");
909 # Convert seconds since 1970 to "yyyy-mm-dd hh:mm:ss" with optional
912 my ($Seconds, $Sep) = @_;
913 defined($Sep) || ($Sep = " ");
914 my @TA = gmtime($Seconds);
915 my($DateString) = sprintf("%04u-%02u-%02u%s%02u:%02u:%02u", $TA[5]+1900, $TA[4]+1, $TA[3], $Sep, $TA[2], $TA[1], $TA[0]);
920 sub sec_to_readable
{
921 # Convert seconds since 1970 to human-readable format (d:hh:mm:ss)
924 D
("sec_to_readable(\"$secs\")\n");
925 my ($Day, $Hour, $Min, $Sec) =
928 $Day = int($secs/86400);
929 $secs -= $Day * 86400;
931 $Hour = int($secs/3600);
932 $secs -= $Hour * 3600;
934 $Min = int($secs/60);
939 return(sprintf("%u:%02u:%02u:%02u", $Day, $Hour, $Min, $Sec));
944 # Send a Postscript header to stdout {{{
945 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
946 my $Date = sec_to_string
(time);
948 "%!PS-Adobe-3.0 EPSF-3.0\n",
949 "%%Creator: $rcs_id\n",
951 "%%CreationDate: $Date\n",
952 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
953 "%%DocumentData: Clean7Bit\n",
956 "/bd { bind def } bind def\n",
957 "/incompound false def\n",
958 "/m { moveto } bd\n",
959 "/l { lineto } bd\n",
960 "/c { curveto } bd\n",
961 "/F { incompound not {fill} if } bd\n",
962 "/f { closepath F } bd\n",
963 "/S { stroke } bd\n",
964 "/*u { /incompound true def } bd\n",
965 "/*U { /incompound false def f} bd\n",
966 "/k { setcmykcolor } bd\n",
976 # Convert floating-point degrees into D°M'S.S" (ISO-8859-1).
977 # Necessary for import into GPSman. Based on toDMS() from
978 # gpstrans-0.39 to ensure compatibility.
982 my ($Hour, $Min, $Sec) =
991 $ddd = ($ddd - $Hour) * 60.0;
993 $Sec = ($ddd - $Min) * 60.0;
1006 D
("Neg = $Neg , D = $Hour , M = $Min , S = $Sec\n");
1007 $Retval = sprintf("%s%.0f\xB0%02.0f'%04.1f\"", $Neg ?
"-" : "", $Hour, $Min, $Sec);
1013 # Convert plain text to XML {{{
1015 $Txt =~ s/&/&/gs;
1016 $Txt =~ s/</</gs;
1017 $Txt =~ s/>/>/gs;
1023 # Convert XML data to plain text {{{
1025 $Txt =~ s/</</gs;
1026 $Txt =~ s/>/>/gs;
1027 $Txt =~ s/&/&/gs;
1033 # Print program version {{{
1040 # Send the help message to stdout {{{
1047 Converts between various GPS formats.
1049 Usage: $progname [options] [file [files [...]]]
1050 $progname -S [file [files [...]]]
1051 $progname -u [file [files [...]]]
1055 -c, --comment-out-dups
1056 Use comma instead of period as decimal point (For Gnumeric etc).
1057 -C, --print-comments
1058 Print existing comment lines (starting with "#") and prefix unknown
1061 Check for broken chronology, warn about entries with an old
1064 Skip duplicated coordinates, only print first and last.
1066 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1068 Comment out entries which is obviously wrong. Use together with
1069 --chronology to fix those kind of errors. Does not work with GPX or
1074 Print only trackpoints inside a rectangle specified by --pos1 and
1077 Use x as undefined value. Default: "$Udef".
1079 Add names of the three closest waypoints to the trackpoint.
1080 Unfinished and experimental, needs gpsbabel.
1081 -o x, --output-format x
1082 Use output format x:
1088 poscount (Experimental)
1095 Print only trackpoints outside a rectangle specified by --pos1 and
1098 Specifies one corner where x is in "lat,lon" format (decimal
1099 degrees, negative for west or south) of area rectangle used by the
1100 --inside and --outside options.
1102 Specify requirements for trackpoints to be written. x is a string
1103 with the following flags:
1105 Print only waypoints which have an altitude.
1107 Print only waypoints which have a timestamp.
1109 Use short date format.
1110 -S x, --save-to-file x
1111 Save the unconverted data to a file with a filename starting with
1112 the timestamp of the first trackpoint. The parameter string x is
1113 added at the end of the filename. For the time being this option
1114 will ignore all other options. Note: If several files are specified
1115 on the command line, all data will be saved into only one file. This
1116 behaviour may change in the future.
1118 Create breaks in track between points with a difference more than
1119 $PAUSE_LIMIT seconds.
1120 -u, --comment-out-dups
1121 Comment out following data with identical position values, only
1124 Verbose, warn about unknown lines.
1126 Print version information.
1127 -w, --strip-whitespace
1128 Strip all unnecessary whitespace.
1129 -y, --double-y-scale
1130 Double Y scale (latitude) to get it right in gnuplot.
1132 Print debugging messages.
1140 # Print a debugging message {{{
1142 my @call_info = caller;
1143 chomp(my $Txt = shift);
1144 my $File = $call_info[1];
1146 $File =~ s
#^.*/(.*?)$#$1#;
1147 print(STDERR
"$File:$call_info[2] $$ $Txt\n");
1154 # Plain Old Documentation (POD) {{{
1168 B<gptrans_conv> [options] [file [files [...]]]
1170 B<gptrans_conv> -S [options] [file [files [...]]]
1172 B<gptrans_conv> -u [options] [file [files [...]]]
1176 Converts between various GPS formats.
1182 =item B<-c>, B<--comment-out-dups>
1184 Use comma instead of period as decimal point (For Gnumeric etc).
1186 =item B<-C>, B<--print-comments>
1188 Print existing comment lines (starting with "#") and prefix unknown
1191 =item B<--chronology>
1193 Check for broken chronology, warn about entries with an old timestamp.
1195 =item B<-d>, B<--skip-dups>
1197 Skip duplicated coordinates, only print first and last.
1199 =item B<-e>, B<--epoch>
1201 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1205 Comment out entries which is obviously wrong. Use together with
1206 --chronology to fix those kind of errors. Does not work with GPX or XML
1209 =item B<-h>, B<--help>
1215 Print only trackpoints inside a rectangle specified by --pos1 and
1220 Add names of the three closest waypoints to the trackpoint. Unfinished
1221 and experimental, needs gpsbabel.
1223 =item B<-n x>, B<--undefined x>
1225 Use x as undefined value.
1227 =item B<-o x>, B<--output-format x>
1229 Use output format x:
1239 =item gpsml (Default)
1241 This is the format which is meant to be used when storing the track
1243 It is line-based XML which makes it easy to edit and grep. Probably not
1248 =item gpx (Not complete)
1250 =item poscount (Experimental)
1252 Creates a 3D plot where areas with many trackpoints are higher than
1253 areas with less track points.
1255 =item ps (Unfinished)
1257 =item svg (Unfinished)
1273 Print only trackpoints outside a rectangle specified by --pos1 and
1276 =item B<--pos1 x>, B<--pos2 x>
1278 Specifies corners of an area rectangle used by the --inside and
1279 --outside options. The x value is in "lat,lon" format (decimal degrees,
1280 negative for west or south) .
1282 =item B<-r x>, B<--require x>
1284 Specify requirements for trackpoints to be written. x is a string with
1285 the following flags:
1295 =item Print only waypoints which have an altitude.
1307 =item Print only waypoints which have a timestamp.
1317 =item B<-s>, B<--short-date>
1319 Use short date format.
1321 =item B<-S x>, B<--save-to-file x>
1323 Save the unconverted data to a file with a filename starting with the
1324 timestamp of the first trackpoint. The parameter string x is added at
1325 the end of the filename. For the time being this option will ignore all
1328 Note: If several files are specified on the command line, all data will
1329 be saved into only one file. This behaviour may change in the future.
1331 =item B<-t>, B<--create-breaks>
1333 Create breaks in track between points with a difference more than the
1334 number of seconds specified by the C<$PAUSE_LIMIT> variable.
1336 =item B<-u>, B<--comment-out-dups>
1338 Comment out following data with identical position values, only print
1341 =item B<-v>, B<--verbose>
1343 Verbose, warn about unknown lines.
1345 =item B<-w>, B<--strip-whitespace>
1347 Strip all unnecessary whitespace.
1349 =item B<-x>, B<--xml>
1353 =item B<-y>, B<--double-y-scale>
1355 Double Y scale (latitude) to get it right in gnuplot.
1357 =item B<-h>, B<--help>
1359 Print a brief help summary.
1363 Print version information.
1367 Print debugging messages.
1373 Pretty incomplete in some areas. Some of the source formats are
1374 undocumented and thus incomplete. Some functionality is not working
1375 properly, for example the Postscript output.
1379 Made by Øyvind A. Holm S<E<lt>sunny@sunbase.orgE<gt>>.
1383 Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1384 This is free software; see the file F<COPYING> for legalese stuff.
1388 This program is free software; you can redistribute it and/or modify it
1389 under the terms of the GNU General Public License as published by the
1390 Free Software Foundation; either version 2 of the License, or (at your
1391 option) any later version.
1393 This program is distributed in the hope that it will be useful, but
1394 WITHOUT ANY WARRANTY; without even the implied warranty of
1395 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1396 See the GNU General Public License for more details.
1398 You should have received a copy of the GNU General Public License along
1399 with this program; if not, write to the Free Software Foundation, Inc.,
1400 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1410 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :