gpst: Legger inn "-T/--time-shift"-valget. Trengte det pga noe som ser
[gpstools.git] / gpst
blobc95ea54eacf99cb780e4b7661dfbc3fdf927e085
1 #!/usr/bin/perl -w
3 #=======================================================================
4 # $Id$
5 # Converts between various GPS formats
7 # Character set: UTF-8
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 #=======================================================================
13 BEGIN {
14 our @version_array;
17 use strict;
18 use Getopt::Long;
19 use Time::Local qw { timegm_nocheck };
21 BEGIN {
22 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
23 our @version_array;
26 use GPST;
27 use GPSTdate;
28 use GPSTdebug;
29 use GPSTgeo;
30 use GPSTxml;
32 $| = 1;
34 our $Debug = 0;
36 our %Opt = (
37 # Initial values for command line arguments {{{
39 'chronology' => 0,
40 'create-breaks' => 0,
41 'debug' => 0,
42 'double-y-scale' => 0,
43 'epoch' => 0,
44 'fix' => 0,
45 'from-date' => "",
46 'help' => 0,
47 'inside' => 0,
48 'near' => "",
49 'output-format' => "gpsml",
50 'outside' => 0,
51 'pos1' => "",
52 'pos2' => "",
53 'require' => "",
54 'round' => "",
55 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
56 'short-date' => 0,
57 'skip-dups' => 0,
58 'strip-whitespace' => 0,
59 'time-shift' => 0,
60 'undefined' => "",
61 'verbose' => 0,
62 'version' => 0,
64 # }}}
67 our $progname = $0;
68 $progname =~ s/^.*\/(.*?)$/$1/;
70 my $rcs_id = '$Id$';
71 my $id_date = $rcs_id;
72 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
74 push(@main::version_array, $rcs_id);
76 Getopt::Long::Configure("bundling");
77 GetOptions(
78 # Command line options {{{
80 "chronology" => \$Opt{'chronology'},
81 "create-breaks|t" => \$Opt{'create-breaks'},
82 "debug" => \$Opt{'debug'},
83 "double-y-scale|y" => \$Opt{'double-y-scale'},
84 "epoch|e" => \$Opt{'epoch'},
85 "fix" => \$Opt{'fix'},
86 "from-date=s" => \$Opt{'from-date'},
87 "help|h" => \$Opt{'help'},
88 "inside" => \$Opt{'inside'},
89 "near" => \$Opt{'near'},
90 "output-format|o=s" => \$Opt{'output-format'},
91 "outside" => \$Opt{'outside'},
92 "pos1=s" => \$Opt{'pos1'},
93 "pos2=s" => \$Opt{'pos2'},
94 "require|r=s" => \$Opt{'require'},
95 "round|R=s" => \$Opt{'round'},
96 "save-to-file|S=s" => \$Opt{'save-to-file'},
97 "short-date|s" => \$Opt{'short-date'},
98 "skip-dups|d" => \$Opt{'skip-dups'},
99 "strip-whitespace|w" => \$Opt{'strip-whitespace'},
100 "time-shift|T=i" => \$Opt{'time-shift'},
101 "undefined|n=s" => \$Opt{'undefined'},
102 "verbose|v+" => \$Opt{'verbose'},
103 "version" => \$Opt{'version'},
105 # }}}
106 ) || die("$progname: Option error. Use -h for help.\n");
108 my %Dat;
110 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
111 my $Udef = "?";
112 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
113 $GPST::Spc = $Opt{'strip-whitespace'} ? "" : " ";
114 my $Spc = $GPST::Spc; # FIXME
115 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
116 my $first_time = 0;
117 my $last_time = 0;
118 my ($last_lon, $last_lat, $last_line) =
119 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
120 my ($lat1, $lon1, $lat2, $lon2) =
121 (-1000, -1000, 1000, 1000);
123 our %Cmd = (
124 'gpsbabel' => '/usr/local/bin/gpsbabel',
127 my %Poscount = ();
129 if ($Opt{'output-format'} eq "pgtab") {
130 $Opt{'require'} .= "p";
132 my %Req = (
133 'ele' => ($Opt{'require'} =~ /e/) ? 1 : 0,
134 'position' => ($Opt{'require'} =~ /p/) ? 1 : 0,
135 'time' => ($Opt{'require'} =~ /t/) ? 1 : 0,
137 $Opt{'require'} =~ /[^ept]/
138 && die("$0: Unknown flag in --require (-r) value\n");
140 $Opt{'debug'} && ($Debug = 1);
141 $Opt{'help'} && usage(0);
142 if ($Opt{'version'}) {
143 print_version();
144 exit(0);
147 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
148 $lat1 = $1;
149 $lon1 = $2;
151 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
152 $lat2 = $1;
153 $lon2 = $2;
155 if ($lat1 > $lat2) {
156 my $Tmp = $lat1;
157 $lat1 = $lat2;
158 $lat2 = $Tmp;
160 if ($lon1 > $lon2) {
161 my $Tmp = $lon1;
162 $lon1 = $lon2;
163 $lon2 = $Tmp;
166 if ($Opt{'epoch'} && $Opt{'short-date'}) {
167 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
170 if ($Opt{'inside'} && $Opt{'outside'}) {
171 die("$progname: Cannot mix the --inside and --outside options\n");
174 # To avoid printing out extra "/> at the start of svg output:
175 my $svg_start_thing = "";
177 my %Round = ();
179 if (defined($Opt{'round'})) {
180 my $R = $Opt{'round'};
181 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
184 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
186 $Opt{'save-to-file'} eq "\n" && print_header(*STDOUT);
188 my @first_lines;
189 my $xml_data;
190 my $data_line = "";
191 our $curr_file = "";
193 my $from_stdin = scalar(@ARGV) ? 0 : 1;
195 $from_stdin && push(@ARGV, "-");
197 for $curr_file (@ARGV) {
198 # Scan through stdin or specified files and send every GPS entry to
199 # print_entry()
200 # {{{
201 print(STDERR "$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
202 if (open(CurrFP, "<$curr_file")) {
203 # {{{
204 while (<CurrFP>) {
205 $data_line = $_;
206 %Dat = (
207 'year' => '', 'month' => '', 'day' => '',
208 'hour' => '', 'min' => '', 'sec' => '',
209 'epoch' => '',
210 'date-format' => '',
211 'lat' => '', 'lon' => '',
212 'ele' => '',
213 'desc' => '',
214 'error' => "",
215 'what' => 'tp',
218 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
219 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
221 if ($Opt{'save-to-file'} ne "\n") {
222 push(@first_lines, $_);
224 s/^# error // && ($Dat{'error'} = "error");
225 s/^# ?// && ($Dat{'error'} = "desc");
226 $xml_data = "";
227 if (m#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
228 # gpsml — The main storage format {{{
229 my ($Elem, $Props, $Data) =
230 ( $1, $2, $3);
231 my $err_str = ($Props =~ /\berr="(.*?)"/) ? $1 : "error";
232 $Elem eq "etp" && ($Dat{'error'} = $err_str);
233 my $Time = "";
234 $Data =~ m#<time>(.*?)</time># && ($Time = $1);
235 $Time =~ s{
236 (\d\d\d\d)-?(\d\d)-?(\d\d)[T ](\d\d):?(\d\d):?([\d\.]+?)Z
238 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
239 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
240 ( $1, $2, $3,
241 $4, $5, $6);
243 }ex;
244 $Data =~ m#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
245 $Data =~ m#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
246 $Data =~ m#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
247 $Data =~ m#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
248 print_entry(%Dat);
249 # }}}
250 } elsif (m#^<break\b.*?/>#) {
251 $found_move = 1;
252 } elsif (m#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
253 $Dat{'what'} = $1;
254 $Dat{$1} = $2;
255 print_entry(%Dat);
256 } elsif (m#^<desc\b.*?>(.*$)#s) {
257 $Dat{'what'} = "desc";
258 my $Txt = $1;
259 until ($Txt =~ m#</desc>#s) {
260 $Txt .= <CurrFP>;
262 $Txt =~ s#^(.*)(</desc>.*$)#$1#s;
263 $Dat{'desc'} = $Txt;
264 print_entry(%Dat);
265 } elsif (/<gpx\b/) {
266 $xml_data = $_;
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);
273 last;
274 } elsif (/^move$/) {
275 $found_move = 1;
276 } elsif (m#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
277 # CSV format, epoch style {{{
278 my ($ep_time, $lon_val, $lat_val, $Alt) =
279 ( $1, $2, $3, $4);
280 $Dat{'epoch'} = $ep_time;
281 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
282 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
283 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
284 $Dat{'month'}++; # Urgh Ⅰ
285 $Dat{'year'} += 1900; # Urgh Ⅱ
286 print_entry(%Dat);
287 # }}}
288 } elsif (
290 (\d\d\d\d)-?(\d\d)-?(\d\d)[T\ ](\d\d):?(\d\d):?(\d\d)Z?\t
291 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
294 # CSV format, human-readable date format {{{
295 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
296 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
297 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
298 ($1, $2, $3,
299 $4, $5, $6,
300 $7, $8, $9);
301 print_entry(%Dat);
302 # }}}
303 } elsif (/^Trackpoint\t/) {
304 # Trackpoint\tN60.41630 E5.31675\t09.02.2006 20:24:37 (UTC)\t13.6 m\t\t93.9 m\t00:00:06\t56 kph\t123° true {{{
306 # Trackpoint\t
307 # N60.41630 E5.31675\t
308 # 09.02.2006 20:24:37 (UTC)\t
309 # 13.6 m\t
310 # \t
311 # 93.9 m\t
312 # 00:00:06\t
313 # 56 kph\t
314 # 123° true
315 my $Orig = $_;
316 $Orig =~ s/[\r\n]+$//;
317 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
318 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
319 split(/\t/, $Orig .
320 # Nødløsning for å unngå at variabler
321 # blir udefinert.
322 "\t\t\t\t\t\t\t\t\t\t"
324 D(join("",
325 "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
326 "Time_f=\"$Time_f\"\n",
327 "Alt_f=\"$Alt_f\"\n",
328 "Depth_f=\"$Depth_f\"\n",
329 "Leglength_f=\"$Leglength_f\"\n",
330 "Legtime_f=\"$Legtime_f\"\n",
331 "Legspeed_f=\"$Legspeed_f\"\n",
332 "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
334 my ($NS, $WE,
335 $Alt_unit,
336 $Leglength,
337 $Legtime_hour, $Legtime_min, $Legtime_sec,
338 $Legspeed, $Legspeed_unit,
339 $Legcourse
340 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
341 "", "", "", "", "", "", "", "", "", "");
342 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
343 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
344 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
345 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
346 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
347 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
348 ($Dat{'ele'} = $1, $Alt_unit = $2);
349 D("ele = \"$Dat{'ele'}\"");
350 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
351 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
352 # MapSource in win xp writes YYYY, but YY in win98se.
354 defined($Dat{'year'})
355 && $Dat{'year'} =~ /\d/
356 && $Dat{'year'} < 1900
357 ) && ($Dat{'year'} += 2000);
358 print_entry(%Dat);
359 # }}}
360 } elsif (/^Track\t(.*?)\t/) {
361 $Dat{'title'} = txt_to_xml($1);
362 $Dat{'what'} = "title";
363 $found_move = 1;
364 print_entry(%Dat);
365 } elsif (
368 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)\t
369 (.+)\xB0(.+)'(.+)"\t
370 (.+)\xB0(.+)'(.+)"
373 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
374 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
375 ($Dat{'month'}, $Dat{'day'}, $Dat{'year'},
376 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
377 $lat_d, $lat_m, $lat_s,
378 $lon_d, $lon_m, $lon_s) =
379 ($1, $2, $3,
380 $4, $5, $6,
381 $7, $8, $9,
382 $10, $11, $12);
383 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
384 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
385 $Dat{'lat'} = sprintf("%${Flat}f",
386 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
387 $Dat{'lon'} = sprintf("%${Flon}f",
388 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
389 print_entry(%Dat);
390 # }}}
391 } elsif (
393 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
394 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
397 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
398 ($Dat{'lat'}, $Dat{'lon'}, $Dat{'speed'},
399 $Dat{'unkn'},
400 $Dat{'month'}, $Dat{'day'}, $Dat{'year'},
401 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
402 ($1, $2, $3,
404 $5, $6, $7,
405 $8, $9, $10);
406 print_entry(%Dat);
407 # }}}
408 } elsif (/^
409 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
410 # Regexp {{{
411 (@) # @
412 (\d\d) # Year
413 (\d\d) # Month
414 (\d\d) # Day
415 (\d\d) # Hours
416 (\d\d) # Minutes
417 (\d\d) # Seconds
418 ([NS]) # N|S
419 (\d\d) # Latitude degree
420 (\d\d) # Latitude minute
421 (\d\d\d) # Latitude minute decimals
422 ([EW]) # E|W
423 (\d\d\d) # Longitude degree
424 (\d\d) # Longitude minute
425 (\d\d\d) # Longitude minute degree
426 (....) # Accurancy
427 (......) # Elevation
428 (...............)
429 # }}}
430 /x) {
431 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
432 $lon_degmin, $lon_mindec);
433 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'}, $Dat{'hour'},
434 $Dat{'min'}, $Dat{'sec'}, $NS, $lat_deg,
435 $lat_degmin, $lat_mindec, $EW,
436 $lon_deg, $lon_degmin, $lon_mindec,
437 $Dat{'accur'}, $Dat{'ele'}, $Dat{'unknown'}) =
438 ($2+2000, $3, $4, $5,
439 $6, $7, $8, $9,
440 $10, $11, $12,
441 $13, $14, $15,
442 $16, $17, $18);
443 my $ep_time = timegm_nocheck(
444 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
445 $Dat{'day'}, $Dat{'month'}-1, $Dat{'year'}
447 $last_time = $ep_time;
448 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
449 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
450 my $tmp_lon = sprintf(
451 "%${Flon}f",
452 $lon_deg +
453 $lon_degmin/60 +
454 $lon_mindec/60000);
455 my $tmp_lat = sprintf("%${Flat}f",
456 $lat_deg +
457 $lat_degmin/60 +
458 $lat_mindec/60000);
459 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
460 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
461 $Dat{'lat'} = $tmp_lat;
462 $Dat{'lon'} = $tmp_lon;
463 print_entry(%Dat);
464 # }}}
465 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
466 # @020721221336__________________________________________ {{{
467 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
468 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'rest'}) =
469 ($2+2000, $3, $4,
470 $5, $6, $7, $8);
471 $Dat{'error'} = "nosignal";
472 print_entry(%Dat);
473 # }}}
474 } elsif (/^xmaplog /) {
475 # NOP
476 } elsif (/^$/) {
477 ($Opt{'output-format'} eq "csv")
478 && ($Opt{'save-to-file'} eq "\n")
479 && print("\n");
480 } elsif (/^Pause: /) {
481 # NOP, is here to cope with old files I’ve lying around.
482 } elsif ($Dat{'error'} eq "desc") {
483 my $Comment = $_;
484 if (defined($Comment)) {
485 $Comment =~ s/^\s*(.*?)\s*$/$1/;
486 if ($Opt{'output-format'} eq "gpsml") {
487 $Dat{'desc'} = txt_to_xml($Comment);
488 $Dat{'what'} = "desc";
489 print_entry(%Dat);
492 } else {
493 $Opt{'verbose'} && warn("Line $.: Unknown: \"$_\"\n");
496 # }}}
497 } else {
498 warn("$progname: $curr_file: Cannot open file for read: $!\n");
500 # }}}
503 print_footer(*STDOUT);
505 exit(0);
507 sub read_xmlfile {
508 # {{{
509 my $Txt = join("", @_);
510 $Txt =~ s/<!--(.*?)-->//gs;
511 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
512 # }}}
515 sub print_gpx {
516 # {{{
517 my $Orig = shift;
518 my $Str = $Orig;
519 # D("print_xml_gps(\"$Orig\")\n");
520 $Str =~ s/<!--(.*?)-->//gs;
521 my $fromdate_str = "";
522 if ($Opt{'from-date'}) {
523 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
525 if ($Opt{'output-format'} =~ /^(pgwtab|pgwupd)$/) {
526 # {{{
527 $Str =~
529 <wpt\b(.*?)>(.*?)</wpt>
532 my $attr_wpt = $1;
533 my $el_wpt = $2;
534 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
535 ('\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N');
537 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
538 ($Lat = postgresql_copy_safe($1));
539 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
540 ($Lon = postgresql_copy_safe($1));
541 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
542 ($Name = postgresql_copy_safe(xml_to_txt($2)));
543 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
544 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
545 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
546 ($Type = postgresql_copy_safe(xml_to_txt($2)));
547 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
548 ($Time = postgresql_copy_safe(xml_to_txt($2)));
549 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
550 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
551 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
552 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
553 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
554 ($Src = postgresql_copy_safe(xml_to_txt($2)));
555 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
556 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
558 if ($Opt{'output-format'} eq "pgwtab") {
559 print(
560 join("\t",
561 "($Lat,$Lon)",
562 $Name,
563 $Ele,
564 $Type,
565 $Time,
566 $Cmt,
567 $Desc,
568 $Src,
569 $Sym,
570 '\N' # Local db id, will be defined when read into wayp_new
571 ) . "\n"
573 } elsif ($Opt{'output-format'} eq "pgwupd") {
574 $Name =~ s/'/''/gs;
575 print(join("\n",
576 "BEGIN;",
577 " UPDATE logg SET sted = clname(coor) " .
578 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
579 " UPDATE logg SET dist = cldist(coor) " .
580 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
581 "COMMIT;"
582 ) . "\n");
585 }gsex;
586 # }}}
587 } else {
588 # {{{
589 $Str =~
591 <trk\b(.*?)>(.*?)</trk>
594 my $el_trk = $2;
595 $el_trk =~
597 <name\b(.*?)>(.*?)</name>
599 my %tmp_dat = ();
600 $tmp_dat{'title'} = $2;
601 $tmp_dat{'what'} = "title";
602 $tmp_dat{'error'} = "";
603 print_entry(%tmp_dat);
605 }sex;
606 $el_trk =~
608 <trkseg\b(.*?)>(.*?)</trkseg>
611 my $el_trkseg = $2;
612 $el_trkseg =~
614 <trkpt\b(.*?)>(.*?)</trkpt>
617 my ($attr_trkpt, $el_trkpt) =
618 ( $1, $2);
619 %Dat = (
620 'year' => '', 'month' => '', 'day' => '',
621 'hour' => '', 'min' => '', 'sec' => '',
622 'epoch' => '',
623 'date-format' => '',
624 'lat' => '', 'lon' => '',
625 'ele' => '',
626 'desc' => '',
627 'error' => "",
628 'what' => 'tp',
630 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon'} = $1);
631 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat'} = $1);
632 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele'} = $1);
633 if (
634 $el_trkpt =~
636 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
637 (\d\d):?(\d\d):?([\d\.]+)Z</time>
640 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
641 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
642 ($1, $2, $3, $4, $5, $6);
644 print_entry(%Dat);
646 }gsex;
647 $found_move = 1;
648 }gsex;
649 $found_move = 1;
650 }gsex;
651 # }}}
653 # }}}
656 sub print_header {
657 # {{{
658 local *OutFP = shift;
659 if ($Opt{'output-format'} eq "gpsml") {
660 print(OutFP join("",
661 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
662 "<gpsml>\n",
663 "<track>\n",
665 } elsif ($Opt{'output-format'} eq "gpstrans") {
666 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
667 "Datum[100]: WGS 84\n");
668 } elsif ($Opt{'output-format'} eq "gpx") {
669 print(OutFP join("",
670 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
671 qq{<gpx\n},
672 qq{$Spc${Spc}version="1.1"\n},
673 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
674 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
675 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
676 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
677 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
678 qq{>\n},
679 qq{$Spc$Spc<trk>\n},
680 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
682 } elsif ($Opt{'output-format'} eq "ps") {
683 print(OutFP ps_header(532, 6034, 533, 6040));
684 print(OutFP "*u\n");
685 } elsif ($Opt{'output-format'} eq "svg") {
686 print(OutFP join("",
687 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
688 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
689 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
690 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
691 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
692 "$Spc$Spc<title></title>\n",
693 "$Spc$Spc<desc></desc>\n",
696 # }}}
699 sub print_footer {
700 # Print footer {{{
701 local *OutFP = shift;
702 if ($Opt{'output-format'} eq "gpsml") {
703 print(OutFP join("",
704 "</track>\n",
705 "</gpsml>\n",
707 } elsif ($Opt{'output-format'} eq "gpx") {
708 print(OutFP join("",
709 "$Spc$Spc$Spc$Spc</trkseg>\n",
710 "$Spc$Spc</trk>\n",
711 "</gpx>\n",
713 } elsif ($Opt{'output-format'} eq "poscount") {
714 while (my ($l_name, $l_val) = each %Poscount) {
715 $l_name =~ /^(.+?),(.+?)$/
716 && print(OutFP "$1\t$2\t$l_val\n");
718 } elsif ($Opt{'output-format'} eq "ps") {
719 print(OutFP join("",
720 "*U\n",
721 "%%Trailer\n",
722 "%%EOF\n",
724 } elsif ($Opt{'output-format'} eq "svg") {
725 print(OutFP "\"/>\n</svg>\n");
727 # }}}
730 sub print_entry {
731 # Print a GPS entry with time, latitude, longitude and elevation in
732 # various formats
733 # {{{
734 my %Dat = @_;
735 defined($Dat{'desc'}) || ($Dat{'desc'} = "");
736 defined($Dat{'ele'}) || ($Dat{'ele'} = "");
737 defined($Dat{'lat'}) || ($Dat{'lat'} = "");
738 defined($Dat{'lon'}) || ($Dat{'lon'} = "");
739 defined($Dat{'year'}) || ($Dat{'year'} = "");
740 my $print_time = length($Dat{'year'}) ? 1 : 0;
741 my $print_pos;
742 if (!$Req{'position'} && $Opt{'output-format'} eq "gpsml") {
743 $print_pos = (length($Dat{'lat'}) || length($Dat{'lon'})) ? 1 : 0;
744 } else {
745 $print_pos = (length($Dat{'lat'}) && length($Dat{'lon'})) ? 1 : 0;
747 if (!$print_pos) {
748 $Dat{'lat'} = $Dat{'lon'} = "";
750 my $print_ele = length($Dat{'ele'}) ? 1 : 0;
751 my $print_desc = length($Dat{'desc'}) ? 1 : 0;
752 my $Line = "";
753 D("print_entry(\"" . join("\", \"", @_) . "\");");
754 my $ep_time;
756 if ($Opt{'near'} && $print_pos) {
757 $Line .= sprintf("%s ",
758 list_nearest_waypoints($Dat{'lat'}, $Dat{'lon'}));
761 if (length($Opt{'round'})) {
762 for my $Tmp (qw{ lat lon ele }) {
763 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
764 D("Tmp = '$Tmp'");
765 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
770 if ($Opt{'output-format'} eq "poscount") {
771 if (!length($Dat{'error'})) {
772 my $Name = "$Dat{'lon'},$Dat{'lat'}";
773 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
774 $Poscount{$Name}++;
776 return;
779 if ($print_time) {
780 $ep_time = timegm_nocheck(
781 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
782 $Dat{'day'}, $Dat{'month'} - 1, $Dat{'year'}
784 if ($Opt{'time-shift'}) {
785 D("ep_time før: '$ep_time'");
786 $ep_time += $Opt{'time-shift'};
787 D("ep_time etter: '$ep_time'");
788 ($Dat{'sec'}, $Dat{'min'},$Dat{'hour'}, $Dat{'day'},
789 $Dat{'month'}, $Dat{'year'}) = gmtime($ep_time);
790 $Dat{'year'} += 1900;
791 $Dat{'month'}++;
793 $Dat{'epoch'} = $ep_time;
794 $Dat{'year'} = sprintf("%04u", $Dat{'year'});
795 $Dat{'month'} = sprintf("%02u", $Dat{'month'});
796 $Dat{'day'} = sprintf("%02u", $Dat{'day'});
797 $Dat{'hour'} = sprintf("%02u", $Dat{'hour'});
798 $Dat{'min'} = sprintf("%02u", $Dat{'min'});
799 $Dat{'sec'} = sprintf("%02u", $Dat{'sec'});
800 if ($Opt{'chronology'}) {
801 if ($last_time > $ep_time && !length($Dat{'error'})) {
802 warn(sprintf(
803 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
804 $progname, sec_to_string($last_time, "T"),
805 sec_to_readable($last_time-$ep_time),
806 sec_to_string($ep_time, "T")
808 # FIXME: Make --fix work with gpx.
809 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^gpx$/)) {
810 $Dat{'error'} = "chrono";
812 } elsif ($last_time == $ep_time && !length($Dat{'error'})) {
813 warn(sprintf(
814 "%s: $curr_file: \"%sZ\": Duplicated time\n",
815 $progname, sec_to_string($last_time, "T")
817 # FIXME: Make --fix work with gpx.
818 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^gpx$/)) {
819 $Dat{'error'} = "duptime";
823 } else {
824 $ep_time = 0;
825 $Dat{'year'} = 0;
826 $Dat{'month'} = 0;
827 $Dat{'day'} = 0;
828 $Dat{'hour'} = 0;
829 $Dat{'min'} = 0;
830 $Dat{'sec'} = 0;
833 if ($Opt{'save-to-file'} ne "\n") {
834 # {{{
835 $print_time || return;
836 my $base_name = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
837 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z" .
838 "$Opt{'save-to-file'}";
839 my $file_name = $base_name;
840 if (-e $file_name) {
841 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
842 $file_name = "$base_name.dup_$a";
844 if (-e $file_name) {
845 die("$progname: $base_name: File already exists, and ran " .
846 "out of attempts to create unique file name\n");
848 if ($Opt{'verbose'}) {
849 warn("$progname: $base_name: File already exists, using " .
850 "unique name \"$file_name\" instead\n");
853 if (open(ToFP, ">", $file_name)) {
854 print_header(*ToFP);
855 print(ToFP (
856 $from_stdin
857 ? @first_lines
858 : ()),
859 (length($xml_data)
860 ? $xml_data
861 : <>)
862 ) || die("$progname: $file_name: Cannot write to file: $!\n");
863 print_footer(*ToFP);
864 close(ToFP);
865 if ($Opt{'output-format'} eq "gpsml") {
866 printf("<include>%s</include>\n",
867 txt_to_xml($file_name));
868 } elsif ($Opt{'output-format'} eq "gpx") {
869 printf("<!-- Saved unconverted data to \"%s\" -->\n",
870 txt_to_xml($file_name));
871 } else {
872 print("$progname: Saved unconverted data to \"$file_name\"\n");
874 exit 0;
875 } else {
876 die("$progname: $file_name: Cannot create file: $!\n");
878 # }}}
881 my $pause_len = 0;
882 my $do_print = 1;
884 if ($Dat{'what'} eq "tp") {
885 # {{{
886 if ($Opt{'require'}) {
887 $Req{'time'} && !$print_time && return;
888 $Req{'position'} && !$print_pos && return;
889 $Req{'ele'} && !$print_ele && return;
892 if ($Opt{'inside'} || $Opt{'outside'}) {
893 if (
894 ($Dat{'lat'} < $lat1) ||
895 ($Dat{'lat'} > $lat2) ||
896 ($Dat{'lon'} < $lon1) ||
897 ($Dat{'lon'} > $lon2)
899 $Opt{'inside'} && return;
900 } else {
901 $Opt{'outside'} && return;
905 if ($Opt{'output-format'} eq "ps") {
906 $Dat{'lon'} *= 100;
907 $Dat{'lat'} *= 100;
910 if (
911 $Opt{'skip-dups'}
912 && ($Dat{'lon'} eq $last_lon)
913 && ($Dat{'lat'} eq $last_lat)
915 if ($Opt{'output-format'} eq 'gpsml') {
916 $Dat{'error'} = "dup";
917 } else {
918 $do_print = 0;
920 } else {
921 $do_print = 1;
924 if (
925 $Opt{'create-breaks'}
926 && $ep_time-$last_time > $PAUSE_LIMIT
927 && $last_time
929 $pause_len = $ep_time-$last_time;
930 D("pause_len set to '$pause_len'");
933 if ($pause_len) {
934 if ($Opt{'output-format'} eq "gpsml") {
935 $Line .= sprintf("<pause>%s</pause>\n",
936 sec_to_readable($ep_time-$last_time));
937 } elsif ($Opt{'output-format'} eq "clean") {
938 $pause_len && ($Line .= "\n");
939 } elsif ($Opt{'output-format'} eq "csv") {
940 $Line .= sprintf("# Pause: %s\n# move\n",
941 sec_to_readable($ep_time-$last_time));
942 } elsif ($Opt{'output-format'} eq "xgraph") {
943 $pause_len && ($Line .= "move ");
946 # }}}
949 if ($do_print) {
950 # Valid data was found, send to stdout {{{
951 unless ($first_time) {
952 $first_time = $ep_time;
954 if ($Opt{'double-y-scale'} && length($Dat{'lat'})) {
955 $Dat{'lat'} *= 2;
957 if ($Opt{'output-format'} eq "gpsml") {
958 if ($Dat{'what'} eq "tp") {
959 $Dat{'format'} = "gpsml";
960 $Line .= trackpoint(%Dat);
961 } elsif ($Dat{'what'} =~ /^(pause|desc|title)$/) {
962 $Line .= sprintf("<%s>%s</%s>\n",
964 $Dat{$1},
965 $1);
967 } elsif ($Opt{'output-format'} eq "pgtab") {
968 if ($Dat{'what'} eq "tp" && !length($Dat{'error'})) {
969 $Dat{'format'} = "pgtab";
970 $Line .= trackpoint(%Dat);
972 } elsif ($Opt{'output-format'} eq "xgraph") {
973 if ($print_pos && !length($Dat{'error'})) {
974 $Dat{'format'} = "xgraph";
975 $Line .= trackpoint(%Dat);
977 } elsif($Opt{'output-format'} eq "gpstrans") {
978 if ($print_pos && !length($Dat{'error'})) {
979 $Dat{'format'} = "gpstrans";
980 $Line .= trackpoint(%Dat);
982 } elsif($Opt{'output-format'} eq "gpx") {
983 if ($Dat{'what'} eq "tp") {
984 $Dat{'format'} = "gpx";
985 $Line .= trackpoint(%Dat);
987 } elsif ($Opt{'output-format'} eq "clean") {
988 if ($Dat{'what'} eq "tp" && !length($Dat{'error'})) {
989 $Dat{'format'} = "clean";
990 $Line .= trackpoint(%Dat);
992 } elsif ($Opt{'output-format'} eq "ps") {
993 $Line .= (
994 $pause_len
995 ? "f\n$Dat{'lon'} $Dat{'lat'} m\n"
996 : "$Dat{'lon'} $Dat{'lat'} l\n"
998 } elsif ($Opt{'output-format'} eq "svg") {
999 $Line .= (
1000 ($last_lon == 1000) || $pause_len
1001 ? join("",
1002 "$svg_start_thing<path\n",
1003 " stroke=\"blue\"\n",
1004 " stroke-width=\"0.001\"\n",
1005 " fill=\"none\"\n",
1006 " d=\"\n",
1007 "M $Dat{'lon'} $Dat{'lat'}\n")
1008 : "L $Dat{'lon'} $Dat{'lat'}\n"
1010 } elsif ($Opt{'output-format'} eq "ygraph") {
1011 if (!length($Dat{'error'})) {
1012 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1013 $Line .= "\"Time = $Time.0\n$Dat{'lon'} $Dat{'lat'}\n\n";
1015 } elsif ($Opt{'output-format'} eq "csv") {
1016 # {{{
1017 if (!length($Dat{'error'})) {
1018 $Dat{'format'} = "csv";
1019 $Line .= join("\t",
1020 $print_time
1021 ? $Opt{'epoch'}
1022 ? $ep_time
1023 : $Opt{'short-date'}
1024 ? "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
1025 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z"
1026 : "$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T" .
1027 "$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z"
1028 : "",
1029 $Dat{'lon'},
1030 $Dat{'lat'},
1031 $print_ele ? $Dat{'ele'} : "", # Elevation
1032 "\n"
1035 # }}}
1036 } elsif ($Opt{'output-format'} eq "pgwtab") {
1037 # FIXME: NOP at the moment.
1038 } else {
1039 die("$progname: \"$Opt{'output-format'}\": " .
1040 "Unknown output format\n");
1042 # }}}
1045 if (!$last_time && $Opt{'output-format'} eq "ps") {
1046 $Line .= "$Dat{'lon'} $Dat{'lat'} m\n";
1049 if ($do_print) {
1050 if ($found_move) {
1051 if ($Opt{'output-format'} eq "gpsml") {
1052 $Line = "<break/>\n$Line";
1054 (!$pause_len && ($Opt{'output-format'} eq "xgraph"))
1055 && ($Line .= "move $Line");
1056 ($Opt{'output-format'} eq "clean") && ($Line .= "\n");
1057 if ($Opt{'output-format'} eq "gpx") {
1058 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1059 "$Spc$Spc$Spc$Spc<trkseg>\n";
1061 $found_move = 0;
1063 print($Line);
1065 $print_time && ($last_time = $ep_time);
1066 if ($print_pos) {
1067 $last_lon = $Dat{'lon'};
1068 $last_lat = $Dat{'lat'};
1070 $last_line = $data_line;
1071 $svg_start_thing = "\"/>\n";
1072 # }}}
1075 sub ps_header {
1076 # Send a Postscript header to stdout {{{
1077 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1078 my $Date = sec_to_string(time);
1079 return(join("",
1080 "%!PS-Adobe-3.0 EPSF-3.0\n",
1081 "%%Creator: $rcs_id\n",
1082 "%%Title:\n",
1083 "%%CreationDate: $Date\n",
1084 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1085 "%%DocumentData: Clean7Bit\n",
1086 "%%EndComments\n",
1087 "%%BeginProlog\n",
1088 "/bd { bind def } bind def\n",
1089 "/incompound false def\n",
1090 "/m { moveto } bd\n",
1091 "/l { lineto } bd\n",
1092 "/c { curveto } bd\n",
1093 "/F { incompound not {fill} if } bd\n",
1094 "/f { closepath F } bd\n",
1095 "/S { stroke } bd\n",
1096 "/*u { /incompound true def } bd\n",
1097 "/*U { /incompound false def f} bd\n",
1098 "/k { setcmykcolor } bd\n",
1099 "/K { k } bd\n",
1100 "%%EndProlog\n",
1101 "%%BeginSetup\n",
1102 "%%EndSetup\n",
1104 # }}}
1107 sub print_version {
1108 # Print program version {{{
1109 for (@main::version_array) {
1110 print("$_\n");
1112 # }}}
1113 } # print_version()
1115 sub usage {
1116 # Send the help message to stdout {{{
1117 my $Retval = shift;
1119 if ($Opt{'verbose'}) {
1120 print("\n");
1121 print_version();
1123 print(<<END);
1125 Converts between various GPS formats.
1127 Usage: $progname [options] [file [files [...]]]
1128 $progname -S [file [files [...]]]
1129 $progname -u [file [files [...]]]
1131 Options:
1133 --chronology
1134 Check for broken chronology, warn about entries with an old
1135 timestamp.
1136 -d, --skip-dups
1137 Skip duplicated coordinates.
1138 -e, --epoch
1139 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1140 --fix
1141 Comment out entries which is obviously wrong. Use together with
1142 --chronology to fix those kind of errors. Does not work with GPX
1143 output yet.
1144 --from-date x
1145 Used by the pgwupd format. Specifies from which date waypoints
1146 should be updated. No checks for valid date format here, let
1147 PostgreSQL take care of that. All variants it understands can be
1148 used here.
1149 -h, --help
1150 Show this help.
1151 --inside
1152 Print only trackpoints inside a rectangle specified by --pos1 and
1153 --pos2.
1154 -n, --undefined x
1155 Use x as undefined value. Default: "$Udef".
1156 --near
1157 Add names of the three closest waypoints to the trackpoint.
1158 Unfinished and experimental, needs gpsbabel, which is called from
1159 the program as "$Cmd{'gpsbabel'}".
1160 -o, --output-format x
1161 Use output format x:
1162 clean
1164 gpsml (Default)
1165 gpstrans
1166 gpx (Not complete)
1167 pgtab
1168 pgwtab
1169 pgwupd
1170 poscount
1171 ps (Unfinished)
1172 svg (Unfinished)
1173 xgraph
1174 ygraph
1175 --outside
1176 Print only trackpoints outside a rectangle specified by --pos1 and
1177 --pos2.
1178 --pos1 x
1179 --pos2 x
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.
1183 -r, --require x
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
1195 -s, --short-date
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.
1204 -t, --create-breaks
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.
1210 -v, --verbose
1211 Increase level of verbosity. Can be repeated.
1212 --version
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.
1218 --debug
1219 Print debugging messages.
1222 exit($Retval);
1223 # }}}
1224 } # usage()
1226 sub msg {
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");
1233 # }}}
1234 } # msg()
1236 __END__
1238 # Law talk {{{
1239 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1241 # This program is free software; you can redistribute it and/or modify
1242 # it under the terms of the GNU General Public License as published by
1243 # the Free Software Foundation; either version 2 of the License, or (at
1244 # your option) any later version.
1246 # This program is distributed in the hope that it will be useful, but
1247 # WITHOUT ANY WARRANTY; without even the implied warranty of
1248 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1249 # See the GNU General Public License for more details.
1251 # You should have received a copy of the GNU General Public License
1252 # along with this program; if not, write to the Free Software
1253 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1254 # USA
1255 # }}}
1257 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
1258 # End of file $Id$