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__ |