| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package GPS::Babel; |
|
2
|
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
65861
|
use warnings; |
|
|
5
|
|
|
|
|
18
|
|
|
|
5
|
|
|
|
|
168
|
|
|
4
|
5
|
|
|
5
|
|
27
|
use strict; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
159
|
|
|
5
|
5
|
|
|
5
|
|
27
|
use Carp; |
|
|
5
|
|
|
|
|
14
|
|
|
|
5
|
|
|
|
|
492
|
|
|
6
|
5
|
|
|
5
|
|
8026
|
use Geo::Gpx 0.15; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use File::Which qw(which); |
|
8
|
|
|
|
|
|
|
use IO::Handle; |
|
9
|
|
|
|
|
|
|
use Scalar::Util qw(blessed); |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.11'; |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $EXENAME = 'gpsbabel'; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
|
16
|
|
|
|
|
|
|
my $class = shift; |
|
17
|
|
|
|
|
|
|
my $args = shift || {}; |
|
18
|
|
|
|
|
|
|
my $self = bless {}, $class; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
if ( exists $args->{exename} ) { |
|
21
|
|
|
|
|
|
|
my $exename = delete $args->{exename}; |
|
22
|
|
|
|
|
|
|
$exename = [$exename] unless ref $exename eq 'ARRAY'; |
|
23
|
|
|
|
|
|
|
$self->set_exename( @$exename ); |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
else { |
|
26
|
|
|
|
|
|
|
$self->set_exename( which( $EXENAME ) || () ); |
|
27
|
|
|
|
|
|
|
} |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
return $self; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub get_exename { |
|
33
|
|
|
|
|
|
|
my $self = shift; |
|
34
|
|
|
|
|
|
|
return @{ $self->{exepath} }; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub set_exename { |
|
38
|
|
|
|
|
|
|
my $self = shift; |
|
39
|
|
|
|
|
|
|
$self->{exepath} = [@_]; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub check_exe { |
|
43
|
|
|
|
|
|
|
my $self = shift; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my @exe = $self->get_exename; |
|
46
|
|
|
|
|
|
|
croak "$EXENAME not found" unless @exe; |
|
47
|
|
|
|
|
|
|
return @exe; |
|
48
|
|
|
|
|
|
|
} |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
sub _with_babel { |
|
51
|
|
|
|
|
|
|
my $self = shift; |
|
52
|
|
|
|
|
|
|
my ( $mode, $opts, $cb ) = @_; |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my @exe = $self->check_exe; |
|
55
|
|
|
|
|
|
|
my $exe_desc = "'" . join( "' '", @exe ) . "'"; |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my @args = ( @exe, @{$opts} ); |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
if ( $^O =~ /MSWin32/ ) { |
|
60
|
|
|
|
|
|
|
# Windows: shell escape and collapse to a single string |
|
61
|
|
|
|
|
|
|
@args = ( '"' . join( '" "', map { s/"/""/g } @args ) . '"' ); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
open( my $fh, $mode, @args ) |
|
65
|
|
|
|
|
|
|
or die "Can't execute $exe_desc ($!)\n"; |
|
66
|
|
|
|
|
|
|
$cb->( $fh ); |
|
67
|
|
|
|
|
|
|
$fh->close or die "$exe_desc failed ($?)\n"; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _with_babel_reader { |
|
71
|
|
|
|
|
|
|
my $self = shift; |
|
72
|
|
|
|
|
|
|
my ( $opts, $cb ) = @_; |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
$self->_with_babel( '-|', $opts, $cb ); |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub _with_babel_lines { |
|
78
|
|
|
|
|
|
|
my $self = shift; |
|
79
|
|
|
|
|
|
|
my ( $opts, $cb ) = @_; |
|
80
|
|
|
|
|
|
|
my @buf = (); |
|
81
|
|
|
|
|
|
|
my $flush = sub { |
|
82
|
|
|
|
|
|
|
my $line = join '', @buf; |
|
83
|
|
|
|
|
|
|
$cb->( $line ) unless $line =~ /^\s*$/; |
|
84
|
|
|
|
|
|
|
@buf = (); |
|
85
|
|
|
|
|
|
|
}; |
|
86
|
|
|
|
|
|
|
$self->_with_babel_reader( |
|
87
|
|
|
|
|
|
|
$opts, |
|
88
|
|
|
|
|
|
|
sub { |
|
89
|
|
|
|
|
|
|
my $fh = shift; |
|
90
|
|
|
|
|
|
|
while ( defined( my $line = <$fh> ) ) { |
|
91
|
|
|
|
|
|
|
chomp $line; |
|
92
|
|
|
|
|
|
|
$flush->() unless $line =~ /^\s+/; |
|
93
|
|
|
|
|
|
|
push @buf, $line; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
); |
|
97
|
|
|
|
|
|
|
$flush->(); |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _with_babel_writer { |
|
101
|
|
|
|
|
|
|
my $self = shift; |
|
102
|
|
|
|
|
|
|
my ( $opts, $cb ) = @_; |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
$self->_with_babel( '|-', $opts, $cb ); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub _tidy { |
|
108
|
|
|
|
|
|
|
my $str = shift; |
|
109
|
|
|
|
|
|
|
$str = '' unless defined $str; |
|
110
|
|
|
|
|
|
|
$str =~ s/^\s+//; |
|
111
|
|
|
|
|
|
|
$str =~ s/\s+$//; |
|
112
|
|
|
|
|
|
|
$str =~ s/\s+/ /g; |
|
113
|
|
|
|
|
|
|
return $str; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _find_info { |
|
117
|
|
|
|
|
|
|
my $self = shift; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
my $info = { |
|
120
|
|
|
|
|
|
|
formats => {}, |
|
121
|
|
|
|
|
|
|
filters => {}, |
|
122
|
|
|
|
|
|
|
for_ext => {} |
|
123
|
|
|
|
|
|
|
}; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Read the version |
|
126
|
|
|
|
|
|
|
$self->_with_babel_reader( |
|
127
|
|
|
|
|
|
|
['-V'], |
|
128
|
|
|
|
|
|
|
sub { |
|
129
|
|
|
|
|
|
|
my $fh = shift; |
|
130
|
|
|
|
|
|
|
local $/; |
|
131
|
|
|
|
|
|
|
$info->{banner} = _tidy( <$fh> ); |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
); |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
if ( $info->{banner} =~ /([\d.]+)/ ) { |
|
136
|
|
|
|
|
|
|
$info->{version} = $1; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
else { |
|
139
|
|
|
|
|
|
|
$info->{version} = '0.0.0'; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
my $handle_extra = sub { |
|
143
|
|
|
|
|
|
|
my @extra = @_; |
|
144
|
|
|
|
|
|
|
return unless @extra; |
|
145
|
|
|
|
|
|
|
my $doclink = shift @extra; |
|
146
|
|
|
|
|
|
|
return ( |
|
147
|
|
|
|
|
|
|
doclink => $doclink, |
|
148
|
|
|
|
|
|
|
@extra ? ( extra => \@extra ) : () |
|
149
|
|
|
|
|
|
|
); |
|
150
|
|
|
|
|
|
|
}; |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# -^3 and -%1 are 1.2.8 and later |
|
153
|
|
|
|
|
|
|
if ( _cmp_ver( $info->{version}, '1.2.8' ) >= 0 ) { |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# File formats |
|
156
|
|
|
|
|
|
|
$self->_with_babel_lines( |
|
157
|
|
|
|
|
|
|
['-^3'], |
|
158
|
|
|
|
|
|
|
sub { |
|
159
|
|
|
|
|
|
|
my $ln = shift; |
|
160
|
|
|
|
|
|
|
my ( $type, @f ) = split( /\t/, $ln ); |
|
161
|
|
|
|
|
|
|
if ( $type eq 'file' ) { |
|
162
|
|
|
|
|
|
|
my ( $modes, $name, $ext, $desc, $parent, @extra, ) = @f; |
|
163
|
|
|
|
|
|
|
( my $nmodes = $modes ) =~ tr/rw-/110/; |
|
164
|
|
|
|
|
|
|
$nmodes = oct( '0b' . $nmodes ); |
|
165
|
|
|
|
|
|
|
$info->{formats}->{$name} = { |
|
166
|
|
|
|
|
|
|
modes => $modes, |
|
167
|
|
|
|
|
|
|
nmodes => $nmodes, |
|
168
|
|
|
|
|
|
|
desc => $desc, |
|
169
|
|
|
|
|
|
|
parent => $parent, |
|
170
|
|
|
|
|
|
|
$handle_extra->( @extra ), |
|
171
|
|
|
|
|
|
|
}; |
|
172
|
|
|
|
|
|
|
if ( $ext ) { |
|
173
|
|
|
|
|
|
|
$ext =~ s/^[.]//; # At least one format has a stray '.' |
|
174
|
|
|
|
|
|
|
$ext = lc( $ext ); |
|
175
|
|
|
|
|
|
|
$info->{formats}->{$name}->{ext} = $ext; |
|
176
|
|
|
|
|
|
|
push @{ $info->{for_ext}->{$ext} }, $name; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
elsif ( $type eq 'option' ) { |
|
180
|
|
|
|
|
|
|
my ( $fname, $name, $desc, $type, $default, $min, $max, |
|
181
|
|
|
|
|
|
|
@extra, ) |
|
182
|
|
|
|
|
|
|
= @f; |
|
183
|
|
|
|
|
|
|
$info->{formats}->{$fname}->{options}->{$name} = { |
|
184
|
|
|
|
|
|
|
desc => $desc, |
|
185
|
|
|
|
|
|
|
type => $type, |
|
186
|
|
|
|
|
|
|
default => $default || '', |
|
187
|
|
|
|
|
|
|
min => $min || '', |
|
188
|
|
|
|
|
|
|
max => $max || '', |
|
189
|
|
|
|
|
|
|
$handle_extra->( @extra ), |
|
190
|
|
|
|
|
|
|
}; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
else { |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Something we don't know about - so ignore it |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
); |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Filters |
|
200
|
|
|
|
|
|
|
$self->_with_babel_lines( |
|
201
|
|
|
|
|
|
|
['-%1'], |
|
202
|
|
|
|
|
|
|
sub { |
|
203
|
|
|
|
|
|
|
my $ln = shift; |
|
204
|
|
|
|
|
|
|
my ( $name, @f ) = split( /\t/, $ln ); |
|
205
|
|
|
|
|
|
|
if ( $name eq 'option' ) { |
|
206
|
|
|
|
|
|
|
my ( $fname, $oname, $desc, $type, @extra ) = @f; |
|
207
|
|
|
|
|
|
|
my @valid = splice @extra, 0, 3; |
|
208
|
|
|
|
|
|
|
$info->{filters}->{$fname}->{options}->{$oname} = { |
|
209
|
|
|
|
|
|
|
desc => $desc, |
|
210
|
|
|
|
|
|
|
type => $type, |
|
211
|
|
|
|
|
|
|
valid => \@valid, |
|
212
|
|
|
|
|
|
|
$handle_extra->( @extra ), |
|
213
|
|
|
|
|
|
|
}; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
else { |
|
216
|
|
|
|
|
|
|
$info->{filters}->{$name} = { desc => $f[0] }; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
); |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
return $info; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub get_info { |
|
226
|
|
|
|
|
|
|
my $self = shift; |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
return $self->{info} ||= $self->_find_info; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub banner { |
|
232
|
|
|
|
|
|
|
my $self = shift; |
|
233
|
|
|
|
|
|
|
return $self->get_info->{banner}; |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub version { |
|
237
|
|
|
|
|
|
|
my $self = shift; |
|
238
|
|
|
|
|
|
|
return $self->get_info->{version}; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _cmp_ver { |
|
242
|
|
|
|
|
|
|
my ( $v1, $v2 ) = @_; |
|
243
|
|
|
|
|
|
|
my @v1 = split( /[.]/, $v1 ); |
|
244
|
|
|
|
|
|
|
my @v2 = split( /[.]/, $v2 ); |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
while ( @v1 && @v2 ) { |
|
247
|
|
|
|
|
|
|
my $cmp = ( shift @v1 <=> shift @v2 ); |
|
248
|
|
|
|
|
|
|
return $cmp if $cmp; |
|
249
|
|
|
|
|
|
|
} |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
return @v1 <=> @v2; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub got_ver { |
|
255
|
|
|
|
|
|
|
my $self = shift; |
|
256
|
|
|
|
|
|
|
my $need = shift; |
|
257
|
|
|
|
|
|
|
my $got = $self->version; |
|
258
|
|
|
|
|
|
|
return _cmp_ver( $got, $need ) >= 0; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub guess_format { |
|
262
|
|
|
|
|
|
|
my $self = shift; |
|
263
|
|
|
|
|
|
|
my $name = shift; |
|
264
|
|
|
|
|
|
|
my $dfmt = shift; |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
croak( "Missing filename" ) |
|
267
|
|
|
|
|
|
|
unless defined( $name ); |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
my $info = $self->get_info; |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Format specified |
|
272
|
|
|
|
|
|
|
if ( defined( $dfmt ) ) { |
|
273
|
|
|
|
|
|
|
croak( "Unknown format \"$dfmt\"" ) |
|
274
|
|
|
|
|
|
|
if %{ $info->{formats} } |
|
275
|
|
|
|
|
|
|
&& !exists( $info->{formats}->{$dfmt} ); |
|
276
|
|
|
|
|
|
|
return $dfmt; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
croak( "Filename \"$name\" has no extension" ) |
|
280
|
|
|
|
|
|
|
unless $name =~ /[.]([^.]+)$/; |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my $ext = lc( $1 ); |
|
283
|
|
|
|
|
|
|
my $fmt = $info->{for_ext}->{$ext}; |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
croak( "No format handles extension .$ext" ) |
|
286
|
|
|
|
|
|
|
unless defined( $fmt ); |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
my @fmt = sort @{$fmt}; |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
return $fmt[0] if @fmt == 1; |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
my $last = pop @fmt; |
|
293
|
|
|
|
|
|
|
my $list = join( ' and ', join( ', ', @fmt ), $last ); |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
croak( "Multiple formats ($list) handle extension .$ext" ); |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub _convert_opts { |
|
299
|
|
|
|
|
|
|
my $self = shift; |
|
300
|
|
|
|
|
|
|
my $inf = shift; |
|
301
|
|
|
|
|
|
|
my $outf = shift; |
|
302
|
|
|
|
|
|
|
my $opts = shift || {}; |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
croak "Must provide input and output filenames" |
|
305
|
|
|
|
|
|
|
unless defined( $outf ); |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
my $infmt = $self->guess_format( $inf, $opts->{in_format} ); |
|
308
|
|
|
|
|
|
|
my $outfmt = $self->guess_format( $outf, $opts->{out_format} ); |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
my $info = $self->get_info; |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
my $inmd = $info->{formats}->{$infmt}->{nmodes} || 0b111111; |
|
313
|
|
|
|
|
|
|
my $outmd = $info->{formats}->{$outfmt}->{nmodes} || 0b111111; |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# Work out which modes can be read by the input format /and/ written by |
|
316
|
|
|
|
|
|
|
# the output format. |
|
317
|
|
|
|
|
|
|
my $canmd = ( $inmd >> 1 ) & $outmd; |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
my @proc = (); |
|
320
|
|
|
|
|
|
|
push @proc, '-r' if ( $canmd & 0x01 ); |
|
321
|
|
|
|
|
|
|
push @proc, '-t' if ( $canmd & 0x04 ); |
|
322
|
|
|
|
|
|
|
push @proc, '-w' if ( $canmd & 0x10 ); |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
croak |
|
325
|
|
|
|
|
|
|
"Formats $infmt and $outfmt have no read/write capabilities in common" |
|
326
|
|
|
|
|
|
|
unless @proc; |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
my @opts = ( |
|
329
|
|
|
|
|
|
|
'-p', '', @proc, '-i', $infmt, '-f', |
|
330
|
|
|
|
|
|
|
$inf, '-o', $outfmt, '-F', $outf |
|
331
|
|
|
|
|
|
|
); |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
return @opts; |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub convert { |
|
337
|
|
|
|
|
|
|
my $self = shift; |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
my @opts = $self->_convert_opts( @_ ); |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
$self->direct( @opts ); |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub direct { |
|
345
|
|
|
|
|
|
|
my $self = shift; |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
if ( system( $self->check_exe, @_ ) ) { |
|
348
|
|
|
|
|
|
|
croak( "$EXENAME failed with error " . ( ( $? == -1 ) ? $! : $? ) ); |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub read { |
|
353
|
|
|
|
|
|
|
my $self = shift; |
|
354
|
|
|
|
|
|
|
my $inf = shift; |
|
355
|
|
|
|
|
|
|
my $opts = shift || {}; |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
require Geo::Gpx; |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
croak "Must provide an input filename" |
|
360
|
|
|
|
|
|
|
unless defined( $inf ); |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
$opts->{out_format} = 'gpx'; |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
my @opts = $self->_convert_opts( $inf, '-', $opts ); |
|
365
|
|
|
|
|
|
|
my $gpx = undef; |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
$self->_with_babel_reader( |
|
368
|
|
|
|
|
|
|
\@opts, |
|
369
|
|
|
|
|
|
|
sub { |
|
370
|
|
|
|
|
|
|
my $fh = shift; |
|
371
|
|
|
|
|
|
|
$gpx = Geo::Gpx->new( input => $fh ); |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
); |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
return $gpx; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub write { |
|
379
|
|
|
|
|
|
|
my $self = shift; |
|
380
|
|
|
|
|
|
|
my $outf = shift; |
|
381
|
|
|
|
|
|
|
my $gpx = shift; |
|
382
|
|
|
|
|
|
|
my $opts = shift || {}; |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
croak "Must provide some data to output" |
|
385
|
|
|
|
|
|
|
unless blessed( $gpx ) && $gpx->can( 'xml' ); |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
$opts->{in_format} = 'gpx'; |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
my $xml = $gpx->xml; |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
my @opts = $self->_convert_opts( '-', $outf, $opts ); |
|
392
|
|
|
|
|
|
|
$self->_with_babel_writer( |
|
393
|
|
|
|
|
|
|
\@opts, |
|
394
|
|
|
|
|
|
|
sub { |
|
395
|
|
|
|
|
|
|
my $fh = shift; |
|
396
|
|
|
|
|
|
|
$fh->print( $xml ); |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
); |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
1; |
|
402
|
|
|
|
|
|
|
__END__ |