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,
39 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
42 'strip-whitespace' => 0,
50 $progname =~ s
#^.*/(.*?)$#$1#;
53 my $id_date = $rcs_id;
54 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
56 Getopt
::Long
::Configure
("bundling");
58 # Command line options {{{
59 "chronology" => \
$Opt{'chronology'},
60 "comment-out-dups|u" => \
$Opt{'comment-out-dups'},
61 "create-breaks|t" => \
$Opt{'create-breaks'},
62 "debug" => \
$Opt{'debug'},
63 "double-y-scale|y" => \
$Opt{'double-y-scale'},
64 "epoch|e" => \
$Opt{'epoch'},
65 "fix" => \
$Opt{'fix'},
66 "help|h" => \
$Opt{'help'},
67 "inside" => \
$Opt{'inside'},
68 "near" => \
$Opt{'near'},
69 "output-format|o=s" => \
$Opt{'output-format'},
70 "outside" => \
$Opt{'outside'},
71 "pos1=s" => \
$Opt{'pos1'},
72 "pos2=s" => \
$Opt{'pos2'},
73 "print-comments|C" => \
$Opt{'print-comments'},
74 "replace" => \
$Opt{'replace'},
75 "require|r=s" => \
$Opt{'require'},
76 "save-to-file|S=s" => \
$Opt{'save-to-file'},
77 "short-date|s" => \
$Opt{'short-date'},
78 "skip-dups|d" => \
$Opt{'skip-dups'},
79 "strip-whitespace|w" => \
$Opt{'strip-whitespace'},
80 "undefined|n=s" => \
$Opt{'undefined'},
81 "use-comma|c" => \
$Opt{'use-comma'},
82 "verbose|v" => \
$Opt{'verbose'},
83 "version" => \
$Opt{'version'},
85 ) || die("$progname: Option error. Use -h for help.\n");
89 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
90 my $Des = $Opt{'use-comma'} ?
"," : ".";
92 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
93 my $Spc = $Opt{'strip-whitespace'} ?
"" : " ";
94 my $in_dupskip = 0; # Er 1 hvis vi holder på med ignorering av duplikater
95 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
98 my ($last_lon, $last_lat, $last_ele, $last_line) =
99 ( 1000, 1000, 100000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
100 my ($lat1, $lon1, $lat2, $lon2) =
101 (-1000, -1000, 1000, 1000);
106 'altitude' => ($Opt{'require'} =~ /a/) ?
1 : 0,
107 'time' => ($Opt{'require'} =~ /t/) ?
1 : 0
109 $Opt{'require'} =~ /[^at]/ && die("$0: Unknown flag in --require (-r) value\n");
111 $Opt{'debug'} && ($Debug = 1);
112 $Opt{'help'} && usage
(0);
113 $Opt{'version'} && print_version
();
115 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
119 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
134 if ($Opt{'inside'} && $Opt{'outside'}) {
135 die("$progname: Cannot mix the --inside and --outside options\n");
138 my $waypoint_file = "/home/sunny/gps/waypoints.gpx";
140 # To avoid printing out extra "/> at the start of svg output:
141 my $svg_start_thing = "";
143 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
144 # Kunne vært et eget script på grunn av at det gjør sine helt egne
145 # greier, men like greit å samle det på en plass.
146 # FIXME: Fjerner ikke første duplikatentryen.
147 # FIXME: Se om det går å få flytta den inn i print_entry() så man
148 # slipper å ha to gptrans_conv’er i pipen.
149 # FIXME: Legg inn alle formatene.
150 if ($Opt{'comment-out-dups'}) {
151 # Comment out areas without reception {{{
152 my ($start_date, $end_date, $found_dup) = ("", "", 0);
155 if (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
157 my ($lat_val, $lon_val, $Speed, $Unkn, $Month, $Day, $Year, $Hour, $Min, $Sec) =
158 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
159 if (($lat_val eq $last_lat) && ($lon_val eq $last_lon)) {
160 unless ($found_dup) {
161 $start_date = "$Year$Month${Day}T$Hour$Min$Sec";
166 $end_date = "$Year$Month${Day}T$Hour$Min$Sec";
169 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
173 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n$_");
179 $last_lat = $lat_val;
180 $last_lon = $lon_val;
191 print("# $start_date-$end_date: CO: No signal \x7B\x7B\x7B\n");
195 print("# $start_date-$end_date: CO: No signal \x7D\x7D\x7D\n# move\n");
202 $Opt{'save-to-file'} eq "\n" && print_header
(*STDOUT
);
209 my $from_stdin = scalar(@ARGV) ?
0 : 1;
211 $from_stdin && push(@ARGV, "-");
213 for $curr_file (@ARGV) {
214 # Scan through stdin or specified files and send every GPS entry to
217 D
("Opening \"$curr_file\" for read");
218 if (open(CurrFP
, "<$curr_file")) {
223 'year' => '', 'month' => '', 'day' => '',
224 'hour' => '', 'min' => '', 'sec' => '',
225 'lat' => '', 'lon' => '',
232 if ($Opt{'save-to-file'} ne "\n") {
233 push(@first_lines, $_);
234 $_ =~ s/^# ?//; # Also read commented-out lines.
237 if (m
#^<(e?tp)\b.*?>(.*?)</(e?tp)>$#) {
238 # gpsml — The main storage format {{{
241 $Elem eq "etp" && ($Dat{'error'} = 1);
243 $Data =~ m
#<time>(.*?)</time># && ($Time = $1);
245 (\d\d\d\d
)-?
(\d\d
)-?
(\d\d
)T
(\d\d
):?
(\d\d
):?
([\d\
.]+?
)Z
247 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
248 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
253 $Data =~ m
#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
254 $Data =~ m
#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
255 $Data =~ m
#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
256 $Data =~ m
#<desc>(.*?)</desc># && ($Dat{'desc'} = xml_to_txt($1));
259 } elsif (m
#^<break\b.*?/>#) {
261 } elsif (m
#^<(desc|title|pause)\b.*?>(.*?)</(desc|title|pause)>#) {
263 $Dat{$1} = xml_to_txt
($2);
265 } elsif (/^<gpx\b/) {
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);
274 } elsif (/^# Pause: /) {
275 $Opt{'print-comments'} && print;
276 } elsif (/^# move$/) {
279 $Opt{'print-comments'} && print;
280 } elsif (m
#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
281 # CSV format, epoch style {{{
282 my ($ep_time, $lon_val, $lat_val, $Alt) =
284 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
285 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
286 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
287 $Dat{'month'}++; # Urgh Ⅰ
288 $Dat{'year'} += 1900; # Urgh Ⅱ
291 } elsif (m
#^(\d\d\d\d)-?(\d\d)-?(\d\d)[T ](\d\d):?(\d\d):?(\d\d)Z?\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
292 # CSV format, human-readable date format {{{
293 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
294 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
295 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
301 } elsif (/^Trackpoint\t/) {
302 # 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 {{{
305 # N60.41630 E5.31675\t
306 # 09.02.2006 20:24:37 (UTC)\t
314 $Orig =~ s/[\r\n]+$//;
315 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
316 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
318 # Nødløsning for å unngå at variabler blir
320 "\t\t\t\t\t\t\t\t\t\t"
323 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
324 "Time_f=\"$Time_f\"\n",
325 "Alt_f=\"$Alt_f\"\n",
326 "Depth_f=\"$Depth_f\"\n",
327 "Leglength_f=\"$Leglength_f\"\n",
328 "Legtime_f=\"$Legtime_f\"\n",
329 "Legspeed_f=\"$Legspeed_f\"\n",
330 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
335 $Legtime_hour, $Legtime_min, $Legtime_sec,
336 $Legspeed, $Legspeed_unit,
338 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
339 "", "", "", "", "", "", "", "");
340 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
341 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
342 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
343 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
344 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
345 ($Alt_f =~ /^([\d+\.]+) (.*?)/) &&
346 ($Dat{'ele'} = $1, $Alt_unit = $2);
347 D
("ele = \"$Dat{'ele'}\"");
348 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
349 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
350 # MapSource in win xp writes YYYY, but YY in win98se.
351 (defined($Dat{'year'}) && $Dat{'year'} =~ /\d/ && $Dat{'year'} < 1900) && ($Dat{'year'} += 2000);
354 } elsif (m
#^T\t(\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)\t(.+)\xB0(.+)'(.+)"\t(.+)\xB0(.+)'(.+)"#) {
355 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
356 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
357 ($Dat{'month'}, $Dat{'day'}, $Dat{'year'},
358 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $lat_d,
359 $lat_m, $lat_s, $lon_d,
361 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
362 $Dat{'lat'} = sprintf("%.5f", 1*($lat_d+($lat_m/60)+($lat_s/3600)));
363 $Dat{'lon'} = sprintf("%.5f", $lon_d+($lon_m/60)+($lon_s/3600));
366 } elsif (m
#^1 (\S+) (\S+) (\S+) (\S+) (\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)#) {
367 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
368 ($Dat{'lat'}, $Dat{'lon'}, $Dat{'speed'},
370 $Dat{'month'}, $Dat{'day'}, $Dat{'year'},
371 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
372 ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
376 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
386 (\d\d
) # Latitude degree
387 (\d\d
) # Latitude minute
388 (\d\d\d
) # Latitude minute decimals
390 (\d\d\d
) # Longitude degree
391 (\d\d
) # Longitude minute
392 (\d\d\d
) # Longitude minute degree
399 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
400 $lon_degmin, $lon_mindec);
401 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'}, $Dat{'hour'},
402 $Dat{'min'}, $Dat{'sec'}, $NS, $lat_deg,
403 $lat_degmin, $lat_mindec, $EW,
404 $lon_deg, $lon_degmin, $lon_mindec,
405 $Dat{'accur'}, $Dat{'ele'}, $Dat{'unknown'}) =
406 ($2+2000, $3, $4, $5, $6, $7, $8, $9, $10, $11,
407 $12, $13, $14, $15, $16, $17, $18);
408 my $ep_time = timegm_nocheck
($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'}, $Dat{'day'}, $Dat{'month'}-1, $Dat{'year'});
409 $last_time = $ep_time;
410 my $tmp_lon = sprintf("%.5f", $lon_deg + $lon_degmin/60 + $lon_mindec/60000);
411 my $tmp_lat = sprintf("%.5f", $lat_deg + $lat_degmin/60 + $lat_mindec/60000);
412 $tmp_lon =~ s/\./$Des/;
413 $tmp_lat =~ s/\./$Des/;
414 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
415 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
416 $Dat{'lat'} = $tmp_lat;
417 $Dat{'lon'} = $tmp_lon;
420 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(__________________________________________)/) {
421 # @020721221336__________________________________________ {{{
422 my ($Alfa, $Year, $Month, $Day, $Hour, $Min, $Sec, $Rest) =
423 ( $1, $2+2000, $3, $4, $5, $6, $7, $8);
424 $Opt{'output-format'} eq "csv" && print("\n");
427 } elsif (/^xmaplog /) {
428 ($Opt{'output-format'} eq "csv") && ($Opt{'save-to-file'} eq "\n") && print("\n");
430 ($Opt{'output-format'} eq "csv") && ($Opt{'save-to-file'} eq "\n") && print("\n");
432 if ($Opt{'print-comments'}) {
436 $Opt{'verbose'} && warn("Line $.: Unknown: \"$_\"\n");
441 warn("$progname: $curr_file: Cannot open file for read: $!\n");
446 print_footer
(*STDOUT
);
452 my $Txt = join("", @_);
453 # FIXME: The sequential stuff here is probably bad, but easy.
454 $Txt =~ s
#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
455 $Txt =~ s
#(<gps\b.*?>.*?</gps>)#print_xml_gps($1)#gse;
463 D
("print_xml_gps(\"$Orig\")\n");
466 <trk
\b(.*?
)>(.*?
)</trk
>
472 <trkseg
\b(.*?
)>(.*?
)</trkseg
>
478 <trkpt
\b(.*?
)>(.*?
)</trkpt
>
481 my ($attr_trkpt, $el_trkpt) =
483 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon'} = $1);
484 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat'} = $1);
485 ($el_trkpt =~ m
#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele'} = $1);
486 if ($el_trkpt =~ m
#<time>(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)\.?(\d*?)Z</time>#) {
487 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
488 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'secfrac'}) =
489 ($1, $2, $3, $4, $5, $6, $7);
505 D
("print_xml_gps(\"$Orig\")\n");
506 if ($Str =~ m
#<date>(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)\.?(\d*?)Z</date>#) {
507 ($Dat{'year'}, $Dat{'mon'}, $Dat{'day'}, $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'secfrac'}) =
508 ( $1, $2, $3, $4, $5, $6, $7);
510 if ($Str =~ m
#<pos>(.*?)</pos>#s) {
512 ($Txt =~ m
#<x\b.*?>(.*?)</x>#) && ($Dat{'lon'} = $1);
513 ($Txt =~ m
#<y\b.*?>(.*?)</y>#) && ($Dat{'lat'} = $1);
514 ($Txt =~ m
#<z\b.*?>(.*?)</z>#) && ($Dat{'ele'} = $1);
516 defined($Dat{'lon'}) || ($Dat{'lon'} = "");
517 defined($Dat{'lat'}) || ($Dat{'lat'} = "");
518 defined($Dat{'ele'}) || ($Dat{'ele'} = "");
525 local *OutFP
= shift;
526 if ($Opt{'output-format'} eq "gpsml") {
528 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
532 } elsif ($Opt{'output-format'} eq "gpstrans") {
533 print(OutFP
"Format: DMS UTC Offset: 0.00 hrs Datum[100]: WGS 84\n");
534 } elsif ($Opt{'output-format'} eq "gpx") {
536 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
539 "$Spc$Spc$Spc$Spc<trkseg>\n",
541 } elsif ($Opt{'output-format'} eq "ps") {
542 print(OutFP ps_header
(532, 6034, 533, 6040));
544 } elsif ($Opt{'output-format'} eq "xml") {
546 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
549 } elsif ($Opt{'output-format'} eq "svg") {
551 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
552 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
553 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
554 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
555 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
556 "$Spc$Spc<title></title>\n",
557 "$Spc$Spc<desc></desc>\n",
565 local *OutFP
= shift;
566 if ($Opt{'output-format'} eq "gpsml") {
571 } elsif ($Opt{'output-format'} eq "gpx") {
573 "$Spc$Spc$Spc$Spc</trkseg>\n",
577 } elsif ($Opt{'output-format'} eq "poscount") {
578 while (my ($l_name, $l_val) = each %Poscount) {
579 $l_name =~ /^(.+?),(.+?)$/ && print(OutFP
"$1\t$2\t$l_val\n");
581 } elsif ($Opt{'output-format'} eq "ps") {
587 } elsif ($Opt{'output-format'} eq "svg") {
588 print(OutFP
"\"/>\n</svg>\n");
589 } elsif ($Opt{'output-format'} eq "xml") {
590 print(OutFP
"</gpslog>\n");
596 # Print a GPS entry with time, latitude, longitude and altitude in
600 my $print_time = length($Dat{'year'}) ?
1 : 0;
601 my $print_ele = length($Dat{'ele'}) ?
1 : 0;
602 my $print_desc = length($Dat{'desc'}) ?
1 : 0;
604 D
("print_entry(\"" . join("\", \"", @_) . "\");");
608 $Line .= sprintf("%s ", list_nearest_waypoints
($Dat{'lat'}, $Dat{'lon'}));
611 if ($Opt{'output-format'} eq "poscount") {
612 my ($Lat_str, $Lon_str) =
614 $Dat{'lon'} =~ /^(\d+\.\d\d)/ && ($Lon_str = $1);
615 $Dat{'lat'} =~ /^(\d+\.\d\d)/ && ($Lat_str = $1);
616 my $Name = "${Lon_str},${Lat_str}";
617 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
623 $ep_time = timegm_nocheck
($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'}, $Dat{'day'}, $Dat{'month'} - 1, $Dat{'year'});
624 $Dat{'year'} = sprintf("%04u", $Dat{'year'});
625 $Dat{'month'} = sprintf("%02u", $Dat{'month'});
626 $Dat{'day'} = sprintf("%02u", $Dat{'day'});
627 $Dat{'hour'} = sprintf("%02u", $Dat{'hour'});
628 $Dat{'min'} = sprintf("%02u", $Dat{'min'});
629 $Dat{'sec'} = sprintf("%02u", $Dat{'sec'});
630 if ($Opt{'chronology'}) {
631 if ($last_time > $ep_time) {
633 "%s: \"%sZ\": Next date is %s in the past (%sZ)\n",
634 $progname, sec_to_string
($last_time, "T"),
635 sec_to_readable
($last_time-$ep_time),
636 sec_to_string
($ep_time, "T")
638 # FIXME: Make --fix work with gpx and xml.
639 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^(gpx|xml)$/)) {
640 ($Line .= "# error ");
645 $Req{'time'} && return;
655 if ($Opt{'save-to-file'} ne "\n") {
657 my $base_name = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z$Opt{'save-to-file'}";
658 my $file_name = $base_name;
660 for (my $a = 1; (-e
$file_name) && ($a < 1000); $a++) {
661 $file_name = "$base_name.dup_$a";
664 die("$progname: $base_name: File already exists, and ran " .
665 "out of attempts to create unique file name\n");
667 if ($Opt{'verbose'}) {
668 warn("$progname: $base_name: File already exists, using " .
669 "unique name \"$file_name\" instead\n");
672 if (open(ToFP
, ">", $file_name)) {
674 print(ToFP
($from_stdin ?
@first_lines : ()), (length($xml_data) ?
$xml_data : <>)) ||
675 die("$progname: $file_name: Cannot write to file: $!\n");
678 if ($Opt{'output-format'} eq "gpsml") {
679 printf("<include>%s</include>\n",
680 txt_to_xml
($file_name));
681 } elsif ($Opt{'output-format'} eq "gpx") {
682 printf("<!-- Saved unconverted data to \"%s\" -->\n",
683 txt_to_xml
($file_name));
685 print("$progname: Saved unconverted data to \"$file_name\"\n");
689 die("$progname: $file_name: Cannot create file: $!\n");
696 ($Req{'altitude'} && !$print_ele) && return;
698 if ($Opt{'inside'} || $Opt{'outside'}) {
700 ($Dat{'lat'} < $lat1) ||
701 ($Dat{'lat'} > $lat2) ||
702 ($Dat{'lon'} < $lon1) ||
703 ($Dat{'lon'} > $lon2)
705 $Opt{'inside'} && return;
707 $Opt{'outside'} && return;
711 if ($Opt{'output-format'} eq "ps") {
715 if ($Opt{'skip-dups'} && ($Dat{'lon'} eq $last_lon) && ($Dat{'lat'} eq $last_lat) && ($Dat{'ele'} eq $last_ele)) {
724 $in_dupskip && ($Line .= $last_line);
728 if ($Opt{'create-breaks'} && $ep_time-$last_time > $PAUSE_LIMIT && $last_time) {
729 $pause_len = $ep_time-$last_time;
730 D
("pause_len set to '$pause_len'");
734 if ($Opt{'output-format'} eq "gpsml") {
735 $Line .= sprintf("<pause>%s</pause>\n",
736 sec_to_readable
($ep_time-$last_time));
737 } elsif ($Opt{'output-format'} eq "csv") {
738 $Line .= sprintf("# Pause: %s\n# move\n",
739 sec_to_readable
($ep_time-$last_time));
744 # Valid data was found, send to stdout {{{
745 unless ($first_time) {
746 $first_time = $ep_time;
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 Replace the processed files with the output. Useful with things like
1110 --fix. WARNING: May fuck up the files at the moment, keep a backup.
1112 Use short date format.
1113 -S x, --save-to-file x
1114 Save the unconverted data to a file with a filename starting with
1115 the timestamp of the first trackpoint. The parameter string x is
1116 added at the end of the filename. For the time being this option
1117 will ignore all other options. Note: If several files are specified
1118 on the command line, all data will be saved into only one file. This
1119 behaviour may change in the future.
1121 Create breaks in track between points with a difference more than
1122 $PAUSE_LIMIT seconds.
1123 -u, --comment-out-dups
1124 Comment out following data with identical position values, only
1127 Verbose, warn about unknown lines.
1129 Print version information.
1130 -w, --strip-whitespace
1131 Strip all unnecessary whitespace.
1132 -y, --double-y-scale
1133 Double Y scale (latitude) to get it right in gnuplot.
1135 Print debugging messages.
1143 # Print a debugging message {{{
1145 my @call_info = caller;
1146 chomp(my $Txt = shift);
1147 my $File = $call_info[1];
1149 $File =~ s
#^.*/(.*?)$#$1#;
1150 print(STDERR
"$File:$call_info[2] $$ $Txt\n");
1157 # Plain Old Documentation (POD) {{{
1171 B<gptrans_conv> [options] [file [files [...]]]
1173 B<gptrans_conv> -S [options] [file [files [...]]]
1175 B<gptrans_conv> -u [options] [file [files [...]]]
1179 Converts between various GPS formats.
1185 =item B<-c>, B<--comment-out-dups>
1187 Use comma instead of period as decimal point (For Gnumeric etc).
1189 =item B<-C>, B<--print-comments>
1191 Print existing comment lines (starting with "#") and prefix unknown
1194 =item B<--chronology>
1196 Check for broken chronology, warn about entries with an old timestamp.
1198 =item B<-d>, B<--skip-dups>
1200 Skip duplicated coordinates, only print first and last.
1202 =item B<-e>, B<--epoch>
1204 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1208 Comment out entries which is obviously wrong. Use together with
1209 --chronology to fix those kind of errors. Does not work with GPX or XML
1212 =item B<-h>, B<--help>
1218 Print only trackpoints inside a rectangle specified by --pos1 and
1223 Add names of the three closest waypoints to the trackpoint. Unfinished
1224 and experimental, needs gpsbabel.
1226 =item B<-n x>, B<--undefined x>
1228 Use x as undefined value.
1230 =item B<-o x>, B<--output-format x>
1232 Use output format x:
1242 =item gpsml (Default)
1244 This is the format which is meant to be used when storing the track
1246 It is line-based XML which makes it easy to edit and grep. Probably not
1251 =item gpx (Not complete)
1253 =item poscount (Experimental)
1255 Creates a 3D plot where areas with many trackpoints are higher than
1256 areas with less track points.
1258 =item ps (Unfinished)
1260 =item svg (Unfinished)
1276 Print only trackpoints outside a rectangle specified by --pos1 and
1279 =item B<--pos1 x>, B<--pos2 x>
1281 Specifies corners of an area rectangle used by the --inside and
1282 --outside options. The x value is in "lat,lon" format (decimal degrees,
1283 negative for west or south) .
1285 =item B<-r x>, B<--require x>
1287 Specify requirements for trackpoints to be written. x is a string with
1288 the following flags:
1298 =item Print only waypoints which have an altitude.
1310 =item Print only waypoints which have a timestamp.
1320 =item B<-s>, B<--short-date>
1322 Use short date format.
1324 =item B<-S x>, B<--save-to-file x>
1326 Save the unconverted data to a file with a filename starting with the
1327 timestamp of the first trackpoint. The parameter string x is added at
1328 the end of the filename. For the time being this option will ignore all
1331 Note: If several files are specified on the command line, all data will
1332 be saved into only one file. This behaviour may change in the future.
1334 =item B<-t>, B<--create-breaks>
1336 Create breaks in track between points with a difference more than the
1337 number of seconds specified by the C<$PAUSE_LIMIT> variable.
1339 =item B<-u>, B<--comment-out-dups>
1341 Comment out following data with identical position values, only print
1344 =item B<-v>, B<--verbose>
1346 Verbose, warn about unknown lines.
1348 =item B<-w>, B<--strip-whitespace>
1350 Strip all unnecessary whitespace.
1352 =item B<-x>, B<--xml>
1356 =item B<-y>, B<--double-y-scale>
1358 Double Y scale (latitude) to get it right in gnuplot.
1360 =item B<-h>, B<--help>
1362 Print a brief help summary.
1366 Print version information.
1370 Print debugging messages.
1376 Pretty incomplete in some areas. Some of the source formats are
1377 undocumented and thus incomplete. Some functionality is not working
1378 properly, for example the Postscript output.
1382 Made by Øyvind A. Holm S<E<lt>sunny@sunbase.orgE<gt>>.
1386 Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1387 This is free software; see the file F<COPYING> for legalese stuff.
1391 This program is free software; you can redistribute it and/or modify it
1392 under the terms of the GNU General Public License as published by the
1393 Free Software Foundation; either version 2 of the License, or (at your
1394 option) any later version.
1396 This program is distributed in the hope that it will be useful, but
1397 WITHOUT ANY WARRANTY; without even the implied warranty of
1398 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1399 See the GNU General Public License for more details.
1401 You should have received a copy of the GNU General Public License along
1402 with this program; if not, write to the Free Software Foundation, Inc.,
1403 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1413 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :