line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
use strict; |
2
|
7
|
|
|
7
|
|
102471
|
use warnings; |
|
7
|
|
|
|
|
37
|
|
|
7
|
|
|
|
|
194
|
|
3
|
7
|
|
|
7
|
|
46
|
|
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
348
|
|
4
|
|
|
|
|
|
|
our $VERSION = '1.02'; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=encoding utf-8 |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Geo::TCX::Trackpoint - Class to store and edit TCX trackpoints |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use Geo::TCX::Trackpoint; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 DESCRIPTION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
This package is mainly used by the L<Geo::TCX> module and serves little purpose on its own. The interface is documented mostly for the purpose of code maintainance. |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
L<Geo::TCX::Trackpoint> provides a data structure for TCX trackpoints and provides accessor methods to read and edit trackpoint data. |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
TCX trackpoints are different from GPX trackpoints in that they contain tags such as C<AltitudeMeters>, C<DistanceMeters>, C<HeartRateBpm>, C<Time>, and potentially C<Cadence>, C<SensorState>. Also the coordinates are tagged with longer-form fields as C<LatitudeDegrees>, C<LongitudeDegrees>. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=cut |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use Geo::Calc; |
27
|
7
|
|
|
7
|
|
3758
|
use Geo::Gpx::Point; |
|
7
|
|
|
|
|
25670023
|
|
|
7
|
|
|
|
|
427
|
|
28
|
7
|
|
|
7
|
|
4812
|
use Carp qw(confess croak cluck); |
|
7
|
|
|
|
|
7813568
|
|
|
7
|
|
|
|
|
307
|
|
29
|
7
|
|
|
7
|
|
74
|
use vars qw($AUTOLOAD %possible_attr); |
|
7
|
|
|
|
|
94
|
|
|
7
|
|
|
|
|
343
|
|
30
|
7
|
|
|
7
|
|
46
|
|
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
6799
|
|
31
|
|
|
|
|
|
|
# file-scoped lexicals |
32
|
|
|
|
|
|
|
my @attr = qw/ LatitudeDegrees LongitudeDegrees /; |
33
|
|
|
|
|
|
|
$possible_attr{$_} = 1 for @attr; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head2 Constructor Methods |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=over 4 |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=item new( $xml_str ) |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Takes an xml string argument containing coordinates contained within the C<Position> xml tag (optional) as recorded by Garmin Edge devices and returns a basic C<Geo::TCX::Trackpoint> object containing only coordinates. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
$str_basic = '<Position><LatitudeDegrees>45.304996</LatitudeDegrees><LongitudeDegrees>-72.637243</LongitudeDegrees></Position>'; |
44
|
|
|
|
|
|
|
$tp_basic = Geo::TCX::Trackpoint->new( $str_basic ); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=item Geo::TCX::Trackpoint::Full::new( $xml_str, $previous_pt ) |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Takes an xml string argument in the form of a Garmin TCX trackpoint, as recorded by Garmin Edge devices, and returns a C<Geo::TCX::Trackpoint::Full> object containing fields that are supplementary to coordinates. See the list of fields in the AUTOLOAD section below. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
$str_full = '<Trackpoint><Time>2014-08-11T10:25:26Z</Time><Position><LatitudeDegrees>45.304996</LatitudeDegrees><LongitudeDegrees>-72.637243</LongitudeDegrees></Position><AltitudeMeters>211.082</AltitudeMeters><DistanceMeters>13.030</DistanceMeters><HeartRateBpm><Value>80</Value></HeartRateBpm></Trackpoint>'; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
$tp_full = Geo::TCX::Trackpoint::Full->new( $str_full ); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
I<$previous_pt> is optional and if specified will be interpreted as the previous trackpoint and be used to keep track of the distance and time that have elapsed since the latter. See the methods below to access these "elapsed" fields. If no previous trackpoint is provided, the elapsed time will remain undefined and the elapsed distance will set to the C<DistanceMeters> field of the trackpoint. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=back |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=cut |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my ($proto, $pt_str) = (shift, shift); |
61
|
|
|
|
|
|
|
croak 'too many arguments specified' if @_; |
62
|
4010
|
|
|
4010
|
1
|
11576
|
my $class = ref($proto) || $proto; |
63
|
4010
|
50
|
|
|
|
9531
|
$pt_str =~ s,\</*Position\>,,g; # Lat and Long are contained in that tag, not needed |
64
|
4010
|
|
33
|
|
|
12218
|
my $pt = {}; |
65
|
4010
|
|
|
|
|
21591
|
bless($pt, $class); |
66
|
4010
|
|
|
|
|
8511
|
|
67
|
4010
|
|
|
|
|
7068
|
# initialize fields/attr |
68
|
|
|
|
|
|
|
while ($pt_str =~ m,\<([^<>]*)\>(.*?)\</([^<>]*)\>,gs) { |
69
|
|
|
|
|
|
|
# or could simply state =~ m,\<(.*?)\>(.*?)\</.*?\>,gs) |
70
|
4010
|
|
|
|
|
19903
|
croak 'Could not match identical attr' unless $1 eq $3; |
71
|
|
|
|
|
|
|
croak 'field not allowed' unless $possible_attr{$1}; |
72
|
8020
|
50
|
|
|
|
21891
|
$pt->{$1} = $2 |
73
|
8020
|
50
|
|
|
|
18532
|
} |
74
|
8020
|
|
|
|
|
33978
|
return $pt |
75
|
|
|
|
|
|
|
} |
76
|
4010
|
|
|
|
|
10809
|
|
77
|
|
|
|
|
|
|
=over 4 |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=item clone() |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Returns a deep copy of a C<Geo::TCX::Trackpoint> instance. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$clone = $trackpoint->clone; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=back |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=cut |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my $clone; |
90
|
|
|
|
|
|
|
eval(Data::Dumper->Dump([ shift ], ['$clone'])); |
91
|
|
|
|
|
|
|
confess $@ if $@; |
92
|
98
|
|
|
98
|
1
|
3680
|
return $clone |
93
|
98
|
|
|
|
|
440
|
} |
94
|
98
|
50
|
|
|
|
554
|
|
95
|
98
|
|
|
|
|
536
|
=head2 AUTOLOAD Methods |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=cut |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=over 4 |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item I<field>( $value ) |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Methods with respect to certain fields can be autoloaded and return the current or newly set value. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
For Basic trackpoints, LatitudeDegrees and LongitudeDegrees are the supported fields. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
For Full trackpoints, supported fields are: LatitudeDegrees, LongitudeDegrees, AltitudeMeters, HeartRateBpm, Cadence, and SensorState. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Some fields may contain a value of 0. It is safer to check if a field is defined with C<< if (defined $trackpoint->Cadence) >> rather than C<< if ($trackpoint->Cadence) >>. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Caution should be used if setting a I<$value> as no checks are performed to ensure the value is appropriate or in the proper format. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=back |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=cut |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
my $self = shift; |
118
|
|
|
|
|
|
|
my $attr = $AUTOLOAD; |
119
|
|
|
|
|
|
|
$attr =~ s/.*:://; |
120
|
|
|
|
|
|
|
return unless $attr =~ /[^A-Z]/; # skip DESTROY and all-cap methods |
121
|
106
|
|
|
106
|
|
198
|
croak "invalid attribute method: -> $attr()" unless $possible_attr{$attr}; |
122
|
106
|
|
|
|
|
157
|
$self->{$attr} = shift if @_; |
123
|
106
|
|
|
|
|
393
|
return $self->{$attr} |
124
|
106
|
100
|
|
|
|
712
|
} |
125
|
66
|
50
|
|
|
|
175
|
|
126
|
66
|
100
|
|
|
|
158
|
=head2 Object Methods |
127
|
66
|
|
|
|
|
651
|
|
128
|
|
|
|
|
|
|
=cut |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=over 4 |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item to_gpx() |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Returns a trackpoint as a L<Geo::Gpx::Point>. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=back |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=cut |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
my ($pt, %attr) = @_; # call to new() will handle error check |
141
|
|
|
|
|
|
|
my %fields = ( lat => $pt->LatitudeDegrees, lon => $pt->LongitudeDegrees ); |
142
|
|
|
|
|
|
|
$fields{ele} = $pt->AltitudeMeters if defined $pt->AltitudeMeters; |
143
|
|
|
|
|
|
|
$fields{time} = $pt->{_time_epoch} if defined $pt->Time; |
144
|
|
|
|
|
|
|
return Geo::Gpx::Point->new( %fields, %attr ); |
145
|
1
|
|
|
1
|
1
|
4
|
} |
146
|
1
|
|
|
|
|
7
|
|
147
|
1
|
50
|
|
|
|
5
|
=over 4 |
148
|
1
|
50
|
|
|
|
4
|
|
149
|
1
|
|
|
|
|
15
|
=item to_geocalc() |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Returns a trackpoint as a L<Geo::Calc> object. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=back |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $pt = shift; |
158
|
|
|
|
|
|
|
croak "to_geocalc() takes no arguments" if @_; |
159
|
|
|
|
|
|
|
return Geo::Calc->new( lat => $pt->LatitudeDegrees, lon => $pt->LongitudeDegrees ); |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=over 4 |
163
|
1
|
|
|
1
|
1
|
973
|
|
164
|
1
|
50
|
|
|
|
7
|
=item to_basic() |
165
|
1
|
|
|
|
|
7
|
|
166
|
|
|
|
|
|
|
Returns a trackpoint as a C<Geo::TCX::Trackpoint> object with only position information (i.e coordinates). |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=back |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=cut |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
my $pt = shift; |
173
|
|
|
|
|
|
|
croak "to_geocalc() takes no arguments" if @_; |
174
|
|
|
|
|
|
|
my $newpt = {}; |
175
|
|
|
|
|
|
|
bless($newpt, 'Geo::TCX::Trackpoint'); |
176
|
|
|
|
|
|
|
$newpt->LatitudeDegrees( $pt->LatitudeDegrees ); |
177
|
|
|
|
|
|
|
$newpt->LongitudeDegrees( $pt->LongitudeDegrees ); |
178
|
|
|
|
|
|
|
return $newpt |
179
|
3
|
|
|
3
|
1
|
481
|
} |
180
|
3
|
50
|
|
|
|
13
|
|
181
|
3
|
|
|
|
|
7
|
=over 4 |
182
|
3
|
|
|
|
|
10
|
|
183
|
3
|
|
|
|
|
16
|
=item distance_to ( $trackpoint ) |
184
|
3
|
|
|
|
|
15
|
|
185
|
3
|
|
|
|
|
21
|
Calculates and returns the distance to the specified I<$trackpoint> object using the L<Geo::Calc> module. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=back |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=cut |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my ($from, $to) = (shift, shift); |
192
|
|
|
|
|
|
|
croak 'expects a single trackpoint as argument' if @_ or ! $to->isa('Geo::TCX::Trackpoint'); |
193
|
|
|
|
|
|
|
my $g = Geo::Calc->new( lat => $from->LatitudeDegrees, lon => $from->LongitudeDegrees ); |
194
|
|
|
|
|
|
|
my $dist = $g->distance_to( { lat => $to->LatitudeDegrees, lon => $to->LongitudeDegrees } ); |
195
|
|
|
|
|
|
|
return $dist |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=over 4 |
199
|
15
|
|
|
15
|
1
|
431
|
|
200
|
15
|
50
|
33
|
|
|
297
|
=item xml_string() |
201
|
15
|
|
|
|
|
212
|
|
202
|
15
|
|
|
|
|
3031
|
returns a string containing the XML representation of the object, equivalent to the string argument expected by C<new()>. |
203
|
15
|
|
|
|
|
194796
|
|
204
|
|
|
|
|
|
|
=back |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
my $pt = shift; |
209
|
|
|
|
|
|
|
my %opts = @_; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
my $newline = $opts{indent} ? "\n" : ''; |
212
|
|
|
|
|
|
|
my $tab = $opts{indent} ? ' ' : ''; |
213
|
|
|
|
|
|
|
my $n_tabs = $opts{n_tabs} ? $opts{n_tabs} : 4; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $str; |
216
|
|
|
|
|
|
|
$str .= $newline . $tab x ($n_tabs + 1) . '<Position>'; |
217
|
0
|
|
|
0
|
1
|
0
|
$str .= $newline . $tab x ($n_tabs + 2) . '<LatitudeDegrees>' . $pt->LatitudeDegrees . '</LatitudeDegrees>'; |
218
|
0
|
|
|
|
|
0
|
$str .= $newline . $tab x ($n_tabs + 2) . '<LongitudeDegrees>' . $pt->LongitudeDegrees . '</LongitudeDegrees>'; |
219
|
|
|
|
|
|
|
$str .= $newline . $tab x ($n_tabs + 1) . '</Position>'; |
220
|
0
|
0
|
|
|
|
0
|
return $str |
221
|
0
|
0
|
|
|
|
0
|
} |
222
|
0
|
0
|
|
|
|
0
|
|
223
|
|
|
|
|
|
|
=over 4 |
224
|
0
|
|
|
|
|
0
|
|
225
|
0
|
|
|
|
|
0
|
=item summ() |
226
|
0
|
|
|
|
|
0
|
|
227
|
0
|
|
|
|
|
0
|
For debugging purposes, summarizes the fields of the trackpoint by printing them to screen. Returns true. |
228
|
0
|
|
|
|
|
0
|
|
229
|
0
|
|
|
|
|
0
|
=back |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=cut |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
my $pt = shift; |
234
|
|
|
|
|
|
|
croak 'summ() expects no arguments' if @_; |
235
|
|
|
|
|
|
|
my %fields; |
236
|
|
|
|
|
|
|
foreach my $key (keys %{$pt}) { |
237
|
|
|
|
|
|
|
print "$key: ", $pt->{$key}, "\n" |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
return 1 |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
use strict; |
243
|
0
|
|
|
0
|
1
|
0
|
use warnings; |
244
|
0
|
0
|
|
|
|
0
|
|
245
|
0
|
|
|
|
|
0
|
use DateTime::Format::ISO8601; |
246
|
0
|
|
|
|
|
0
|
use Carp qw(confess croak cluck); |
|
0
|
|
|
|
|
0
|
|
247
|
0
|
|
|
|
|
0
|
|
248
|
|
|
|
|
|
|
our $VERSION = '1.02'; |
249
|
0
|
|
|
|
|
0
|
our @ISA=qw(Geo::TCX::Trackpoint); |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
{ # lexical scope for that package |
253
|
7
|
|
|
7
|
|
69
|
|
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
197
|
|
254
|
7
|
|
|
7
|
|
45
|
use vars qw($AUTOLOAD %possible_attr); |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
354
|
|
255
|
|
|
|
|
|
|
|
256
|
7
|
|
|
7
|
|
51
|
our ($LocalTZ, $Formatter); |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
246
|
|
257
|
7
|
|
|
7
|
|
43
|
$LocalTZ = DateTime::TimeZone->new( name => 'local' ); |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
671
|
|
258
|
|
|
|
|
|
|
$Formatter = DateTime::Format::Strptime->new( pattern => '%a %b %e %H:%M:%S %Y' ); |
259
|
|
|
|
|
|
|
my $formatter_xsd = DateTime::Format::Strptime->new( pattern => '%Y-%m-%dT%H:%M:%SZ' ); |
260
|
|
|
|
|
|
|
# ... to avoid looking up timezone each time Trackpoint->new is called |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# file-scoped lexicals |
263
|
|
|
|
|
|
|
my @attr = qw/ LatitudeDegrees LongitudeDegrees AltitudeMeters DistanceMeters Time HeartRateBpm Cadence SensorState /; |
264
|
|
|
|
|
|
|
$possible_attr{$_} = 1 for @attr; |
265
|
7
|
|
|
7
|
|
50
|
|
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
16029
|
|
266
|
|
|
|
|
|
|
my ($proto, $pt_str, $previous_pt) = (shift, shift, shift); |
267
|
|
|
|
|
|
|
if (ref $previous_pt) { |
268
|
|
|
|
|
|
|
croak 'second argument must be a Trackpoint object' unless $previous_pt->isa('Geo::TCX::Trackpoint') |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
croak 'too many arguments specified' if @_; |
271
|
|
|
|
|
|
|
my $class = ref($proto) || $proto; |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
my $chomped_str = $pt_str; |
274
|
|
|
|
|
|
|
if ( $chomped_str =~ m,\s*^\<Trackpoint\>(.*)\</Trackpoint\>\s*$,gs ) { |
275
|
|
|
|
|
|
|
$chomped_str = $1 |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
# contrary to Track, the <Trackpoint>...</Trackpoint> are optional |
278
|
3980
|
|
|
3980
|
|
17522
|
|
279
|
3980
|
100
|
|
|
|
10487
|
# Extract the Position tag and create a basic positional trackpoint |
280
|
3887
|
50
|
|
|
|
13277
|
my $pt; |
281
|
|
|
|
|
|
|
if ( $chomped_str =~ s/(<Position>.*<\/Position>)//g ) { |
282
|
3980
|
50
|
|
|
|
8595
|
$pt =$class->SUPER::new( $1 ) |
283
|
3980
|
|
33
|
|
|
13753
|
} else { |
284
|
|
|
|
|
|
|
# $DB::single=1; |
285
|
3980
|
|
|
|
|
5854
|
# I put a debug flag here because I want to see instances where |
286
|
3980
|
50
|
|
|
|
22645
|
# a trackpoint does not have coordinates and see how I should address those |
287
|
3980
|
|
|
|
|
11433
|
# croak 'no <Position>...</Position> xml tag in string' |
288
|
|
|
|
|
|
|
# call it anyway for now until I figure out how to handle those |
289
|
|
|
|
|
|
|
$pt = {}; |
290
|
|
|
|
|
|
|
bless($pt, $class); |
291
|
|
|
|
|
|
|
} |
292
|
3980
|
|
|
|
|
5741
|
$chomped_str =~ s,\</*Value\>,,g; # HeartRateBpm value contained in that tag, not needed |
293
|
3980
|
100
|
|
|
|
28682
|
|
294
|
3979
|
|
|
|
|
11608
|
# initialize fields/attr |
295
|
|
|
|
|
|
|
while ($chomped_str=~ m,\<([^<>]*)\>(.*?)\</([^<>]*)\>,gs) { |
296
|
|
|
|
|
|
|
# or could simply state =~ m,\<(.*?)\>(.*?)\</.*?\>,gs) |
297
|
|
|
|
|
|
|
croak 'Could not match identical attr' unless $1 eq $3; |
298
|
|
|
|
|
|
|
croak 'field not allowed' unless $possible_attr{$1}; |
299
|
|
|
|
|
|
|
$pt->{$1} = $2 |
300
|
|
|
|
|
|
|
} |
301
|
1
|
|
|
|
|
2
|
|
302
|
1
|
|
|
|
|
3
|
# for debugging -- allow trackpoints with only coordinates but inspect them in debugger |
303
|
|
|
|
|
|
|
$pt->{_noTime} = 1 unless defined $pt->{Time}; |
304
|
3980
|
|
|
|
|
24883
|
$pt->{_noDist} = 1 unless defined $pt->{DistanceMeters}; |
305
|
|
|
|
|
|
|
if ($pt->{_noTime} or $pt->{_noDist}) { |
306
|
|
|
|
|
|
|
# commented out as I am building my databases, way too many files to parse to inspect them now, will uncomment when I am done parsing my databases |
307
|
3980
|
|
|
|
|
16903
|
# $DB::single=1 |
308
|
|
|
|
|
|
|
} |
309
|
15032
|
50
|
|
|
|
35028
|
|
310
|
15032
|
50
|
|
|
|
31129
|
$pt->_reset_distance( $pt->{DistanceMeters}, $previous_pt ) unless $pt->{_noDist}; |
311
|
15032
|
|
|
|
|
62201
|
unless ($pt->{_noTime}) { |
312
|
|
|
|
|
|
|
my $orig_time_string = $pt->{Time}; |
313
|
|
|
|
|
|
|
$pt->_reset_time( $pt->{Time}, $previous_pt ) unless $pt->{_noTime}; |
314
|
|
|
|
|
|
|
print "strange ISO time not equal to time string from TCX file for this trackpoint\n" |
315
|
3980
|
50
|
|
|
|
8869
|
if $orig_time_string ne $pt->{_time_iso8601}; |
316
|
3980
|
50
|
|
|
|
7863
|
} |
317
|
3980
|
50
|
33
|
|
|
15257
|
return $pt |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
my $self = shift; |
321
|
|
|
|
|
|
|
my $attr = $AUTOLOAD; |
322
|
3980
|
50
|
|
|
|
14812
|
$attr =~ s/.*:://; |
323
|
3980
|
50
|
|
|
|
8580
|
return unless $attr =~ /[^A-Z]/; # skip DESTROY and all-cap methods |
324
|
3980
|
|
|
|
|
6982
|
croak "invalid attribute method: -> $attr()" unless $possible_attr{$attr}; |
325
|
3980
|
50
|
|
|
|
13230
|
$self->{$attr} = shift if @_; |
326
|
|
|
|
|
|
|
return $self->{$attr} |
327
|
3980
|
50
|
|
|
|
11064
|
} |
328
|
|
|
|
|
|
|
|
329
|
3980
|
|
|
|
|
11966
|
=head2 Object Methods for class Geo::TXC::Trackpoint::Full |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=over 4 |
332
|
|
|
|
|
|
|
|
333
|
19567
|
|
|
19567
|
|
35661
|
=item DistanceMeters() |
334
|
19567
|
|
|
|
|
24540
|
|
335
|
19567
|
|
|
|
|
51628
|
Returns the C<DistanceMeters> field of a trackpoint. |
336
|
19567
|
100
|
|
|
|
101410
|
|
337
|
11217
|
50
|
|
|
|
20589
|
=back |
338
|
11217
|
50
|
|
|
|
18607
|
|
339
|
11217
|
|
|
|
|
27688
|
=cut |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=over 4 |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=item distance_elapsed( $value, force => true/false ) |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Returns the elapsed distance (in meters) of a point as initially computed when the trackpoint was created. The value is never reset unless C<< force => 1 >> is specified. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
C<force> is needed internally by L<Geo::TCX::Lap>'s C<split()> and L<Geo::TCX::Track>'s <merge()> methods. Use with caution. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=back |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=cut |
353
|
|
|
|
|
|
|
|
354
|
10728
|
|
|
10728
|
|
29843
|
my ($pt, $value) = (shift, shift); |
355
|
|
|
|
|
|
|
my %opts = @_; |
356
|
|
|
|
|
|
|
if (defined $value) { |
357
|
|
|
|
|
|
|
croak "need to specify option 'force => 1' to set a value" unless $opts{force}; |
358
|
|
|
|
|
|
|
$pt->{_distance_elapsed} = sprintf '%.3f', $value |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
return $pt->{_distance_elapsed} |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=over 4 |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=item Time() |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Returns the C<Time> field of a trackpoint. |
368
|
|
|
|
|
|
|
|
369
|
1057
|
|
|
1057
|
|
1791
|
=back |
370
|
1057
|
|
|
|
|
1650
|
|
371
|
1057
|
100
|
|
|
|
1839
|
=cut |
372
|
2
|
50
|
|
|
|
19
|
|
373
|
2
|
|
|
|
|
14
|
|
374
|
|
|
|
|
|
|
=over 4 |
375
|
|
|
|
|
|
|
|
376
|
1057
|
|
|
|
|
3020
|
=item time_dt () |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=item time_datetime () |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Return a L<DateTime> object corresponding to the time of a trackpoint. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=back |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=cut |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# we never store a DateTime object but provide a method to create one |
387
|
|
|
|
|
|
|
|
388
|
6120
|
|
|
6120
|
|
26116
|
=over 4 |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=item time_local( $trackpoint ) |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Returns the formatted local time of the trackpoint. The local time is always represented based on the locale of the system that calls this method, not that of where the trackpoint was recorded. It is not possible to know in which time zone a trackpoint was recorded at this stage. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=back |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=cut |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=over 4 |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=item time_add( @duration ) |
402
|
0
|
|
|
0
|
|
0
|
|
403
|
4848
|
|
|
4848
|
|
9989
|
=item time_subtract( @duration ) |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Perform L<DateTime> math on the timestamps of each lap's starttime and trackpoint by adding the specified time duration and return true. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
The duration can be provided as an actual L<DateTime::Duration> object or an array of arguments as per the syntax of L<DateTime>'s C<add()> or C<subtract()> methods, which expect a hash of keys such as |
408
|
|
|
|
|
|
|
years => 3, |
409
|
|
|
|
|
|
|
months => 5, |
410
|
|
|
|
|
|
|
weeks => 1, |
411
|
|
|
|
|
|
|
days => 1, |
412
|
|
|
|
|
|
|
hours => 6, |
413
|
|
|
|
|
|
|
minutes => 15, |
414
|
|
|
|
|
|
|
seconds => 45, |
415
|
|
|
|
|
|
|
nanoseconds => 12000, |
416
|
2
|
|
|
2
|
|
9
|
end_of_month => 'limit' |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
where only the relevant keys need to be specified i.e. C<< time_add( minutes > 30, seconds > 15) >>. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=back |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=cut |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
my ($pt, $dur) = shift; |
425
|
|
|
|
|
|
|
if (ref $_[0] and $_[0]->isa('DateTime::Duration') ) { |
426
|
|
|
|
|
|
|
$dur = shift |
427
|
|
|
|
|
|
|
} else { $dur = DateTime::Duration->new( @_ ) } |
428
|
|
|
|
|
|
|
my $dt = $pt->time_datetime; |
429
|
|
|
|
|
|
|
$dt->add( $dur ); |
430
|
|
|
|
|
|
|
$pt->_set_time_keys( $dt ); |
431
|
|
|
|
|
|
|
return 1 |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
my ($pt, $dur) = shift; |
435
|
|
|
|
|
|
|
if (ref $_[0] and $_[0]->isa('DateTime::Duration') ) { |
436
|
|
|
|
|
|
|
$dur = shift |
437
|
|
|
|
|
|
|
} else { $dur = DateTime::Duration->new( @_ ) } |
438
|
|
|
|
|
|
|
my $dt = $pt->time_datetime; |
439
|
|
|
|
|
|
|
$dt->subtract( $dur ); |
440
|
|
|
|
|
|
|
$pt->_set_time_keys( $dt ); |
441
|
|
|
|
|
|
|
return 1 |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
436
|
|
|
436
|
|
965
|
=over 4 |
445
|
436
|
100
|
66
|
|
|
1487
|
|
446
|
63
|
|
|
|
|
108
|
=item time_epoch() |
447
|
373
|
|
|
|
|
1320
|
|
448
|
436
|
|
|
|
|
37386
|
Returns the epoch time of a point. |
449
|
436
|
|
|
|
|
200248
|
|
450
|
436
|
|
|
|
|
374935
|
=back |
451
|
436
|
|
|
|
|
4125
|
|
452
|
|
|
|
|
|
|
=cut |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
455
|
423
|
|
|
423
|
|
958
|
=over 4 |
456
|
423
|
100
|
66
|
|
|
1533
|
|
457
|
63
|
|
|
|
|
112
|
=item time_elapsed( $value, force => true/false ) |
458
|
360
|
|
|
|
|
1238
|
|
459
|
423
|
|
|
|
|
35743
|
Returns the elapsed time of a point as initially computed when the trackpoint was created. The value is never reset unless C<< force => 1 >> is specified. |
460
|
423
|
|
|
|
|
193356
|
|
461
|
423
|
|
|
|
|
416077
|
C<force> is needed internally by L<Geo::TCX::Lap>'s constructor, C<split()>, and C<reverse()> methods as well as L<Geo::TCX::Track>'s <reverse()>. Use with caution. |
462
|
423
|
|
|
|
|
4224
|
|
463
|
|
|
|
|
|
|
=back |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=cut |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my ($pt, $value) = (shift, shift); |
468
|
|
|
|
|
|
|
my %opts = @_; |
469
|
|
|
|
|
|
|
if (defined $value) { |
470
|
|
|
|
|
|
|
croak "need to specify option 'force => 1' to set a value" unless $opts{force}; |
471
|
|
|
|
|
|
|
$pt->{_time_elapsed} = $value |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
return $pt->{_time_elapsed} |
474
|
|
|
|
|
|
|
} |
475
|
140
|
|
|
140
|
|
418
|
|
476
|
|
|
|
|
|
|
=over 4 |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=item time_duration( $datetime or $trackpoint or $string or $integer ) |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Returns a L<DateTime::Duration> object containing the duration between the timestamps of two trackpoints. Consistent with the documentation for L<DateTime::Duration> the "duration is relative to the object from which I<$datetime> is subtracted". The duration will be positive if the timestamp of I<$datetime> occurs prior to the trackpoint, otherwise it will be negative. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
This method accepts four forms for the argument: a L<DateTime> object such as that returned by C<< $pt->time >>, an ISO8601 string such as that returned by C<< $pt->Time >>, a Trackpoint object, or an integer than can be interpreted as an epoch time. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
These duration objects are useful to pass to C<time_add()> or C<time_subtract>. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=back |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=cut |
489
|
|
|
|
|
|
|
|
490
|
4724
|
|
|
4724
|
|
7093
|
my $self = shift; |
491
|
4724
|
|
|
|
|
6626
|
my ($dt, $datetime); |
492
|
4724
|
100
|
|
|
|
7912
|
# first arg can time DateTime or trackpoint, and epoch time, or a time string |
493
|
58
|
50
|
|
|
|
203
|
if (ref $_[0]) { |
494
|
58
|
|
|
|
|
169
|
if ( $_[0]->isa('DateTime') ) { |
495
|
|
|
|
|
|
|
$datetime = $_[0] |
496
|
|
|
|
|
|
|
} else { |
497
|
4724
|
|
|
|
|
10937
|
croak 'object as argument must be either a DateTime or a Trackpoint instance' |
498
|
|
|
|
|
|
|
unless $_[0]->isa('Geo::TCX::Trackpoint'); |
499
|
|
|
|
|
|
|
$datetime = $_[0]->time_datetime |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
} elsif ($_[0] =~ /^(\d+)$/) { |
502
|
|
|
|
|
|
|
$datetime = DateTime->from_epoch( epoch => $1 ) |
503
|
|
|
|
|
|
|
} else { |
504
|
|
|
|
|
|
|
$datetime = DateTime::Format::ISO8601->parse_datetime( $_[0] ) |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
$dt = $self->time_datetime; |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
my $dur = $dt->subtract_datetime( $datetime ); |
509
|
|
|
|
|
|
|
return $dur |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
my $pt = shift; |
513
|
|
|
|
|
|
|
my %opts = @_; |
514
|
5
|
|
|
5
|
|
12
|
|
515
|
5
|
|
|
|
|
11
|
my $newline = $opts{indent} ? "\n" : ''; |
516
|
|
|
|
|
|
|
my $tab = $opts{indent} ? ' ' : ''; |
517
|
5
|
100
|
|
|
|
30
|
my $n_tabs = $opts{n_tabs} ? $opts{n_tabs} : 4; |
|
|
100
|
|
|
|
|
|
518
|
2
|
50
|
|
|
|
17
|
|
519
|
0
|
|
|
|
|
0
|
my $str; |
520
|
|
|
|
|
|
|
$str .= $newline . $tab x $n_tabs . '<Trackpoint>'; |
521
|
2
|
50
|
|
|
|
11
|
$str .= $newline . $tab x ($n_tabs + 1) . '<Time>' . $pt->Time . '</Time>'; |
522
|
|
|
|
|
|
|
if (defined $pt->LatitudeDegrees) { |
523
|
2
|
|
|
|
|
9
|
$str .= $newline . $tab x ($n_tabs + 1) . '<Position>'; |
524
|
|
|
|
|
|
|
$str .= $newline . $tab x ($n_tabs + 2) . '<LatitudeDegrees>' . $pt->LatitudeDegrees . '</LatitudeDegrees>'; |
525
|
|
|
|
|
|
|
$str .= $newline . $tab x ($n_tabs + 2) . '<LongitudeDegrees>' . $pt->LongitudeDegrees . '</LongitudeDegrees>'; |
526
|
1
|
|
|
|
|
6
|
$str .= $newline . $tab x ($n_tabs + 1) . '</Position>'; |
527
|
|
|
|
|
|
|
} |
528
|
2
|
|
|
|
|
8
|
$str .= $newline . $tab x ($n_tabs + 1) . '<AltitudeMeters>'. $pt->AltitudeMeters . '</AltitudeMeters>'; |
529
|
|
|
|
|
|
|
$str .= $newline . $tab x ($n_tabs + 1) . '<DistanceMeters>'. $pt->DistanceMeters . '</DistanceMeters>'; |
530
|
5
|
|
|
|
|
2221
|
if (defined $pt->HeartRateBpm) { |
531
|
|
|
|
|
|
|
$str .= '<HeartRateBpm><Value>'. $pt->HeartRateBpm . '</Value></HeartRateBpm>' |
532
|
5
|
|
|
|
|
2019
|
} |
533
|
5
|
|
|
|
|
1604
|
if (defined $pt->Cadence) { |
534
|
|
|
|
|
|
|
$str .= '<Cadence>'. $pt->Cadence . '</Cadence>' |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
if (defined $pt->SensorState) { |
537
|
1238
|
|
|
1238
|
|
1797
|
$str .= '<SensorState>'. $pt->SensorState . '</SensorState>' |
538
|
1238
|
|
|
|
|
2710
|
} |
539
|
|
|
|
|
|
|
$str .= $newline . $tab x $n_tabs . '</Trackpoint>'; |
540
|
1238
|
100
|
|
|
|
2486
|
return $str |
541
|
1238
|
100
|
|
|
|
2384
|
} |
542
|
1238
|
50
|
|
|
|
2142
|
|
543
|
|
|
|
|
|
|
# Internal methods and functions |
544
|
1238
|
|
|
|
|
1560
|
|
545
|
1238
|
|
|
|
|
2500
|
my ($pt, $time, $previous_pt) = @_; |
546
|
1238
|
|
|
|
|
2796
|
$previous_pt = pop if ref $_[-1] and $_[-1]->isa('Geo::TCX::Trackpoint'); |
547
|
1238
|
100
|
|
|
|
3819
|
delete $pt->{_time_elapsed}; # by design, immutable in _set_* |
548
|
1237
|
|
|
|
|
2635
|
$pt->_set_time_keys($time, $previous_pt); |
549
|
1237
|
|
|
|
|
3959
|
return 1 |
550
|
1237
|
|
|
|
|
4242
|
} |
551
|
1237
|
|
|
|
|
3215
|
|
552
|
|
|
|
|
|
|
my ($pt, $epoch, $previous_pt) = @_; |
553
|
1238
|
|
|
|
|
4131
|
my $dt = DateTime->from_epoch( epoch => $epoch ); |
554
|
1238
|
|
|
|
|
3157
|
delete $pt->{_time_elapsed}; |
555
|
1238
|
100
|
|
|
|
3411
|
$pt->_set_time_keys( $dt, $previous_pt ); |
556
|
1023
|
|
|
|
|
3047
|
return 1 |
557
|
|
|
|
|
|
|
} |
558
|
1238
|
50
|
|
|
|
3655
|
|
559
|
0
|
|
|
|
|
0
|
my ($pt, $distance, $previous_pt) = @_; |
560
|
|
|
|
|
|
|
if (ref $previous_pt) { |
561
|
1238
|
50
|
|
|
|
3715
|
croak 'second argument must be a Trackpoint object' unless $previous_pt->isa('Geo::TCX::Trackpoint') |
562
|
0
|
|
|
|
|
0
|
} |
563
|
|
|
|
|
|
|
delete $pt->{_distance_elapsed}; |
564
|
1238
|
|
|
|
|
2545
|
$pt->_set_distance_keys($distance, $previous_pt); |
565
|
1238
|
|
|
|
|
4115
|
return 1 |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# Expects a I<$time_string> in a format parseable by L<DateTime::Format::ISO8601>'s C<parse_datetime> constructor |
569
|
|
|
|
|
|
|
# . sets the time-related fields for the trackpoint. Returns true. |
570
|
|
|
|
|
|
|
# . if the _time_elapsed key for the point is not already defined and another trackpoint object is also provided, |
571
|
3982
|
|
|
3982
|
|
7664
|
# e.g. the previous trackpoint, it will also set it (as number of seconds since the timestamp of that previous point) |
572
|
3982
|
100
|
66
|
|
|
18728
|
# . allows a DateTime obj as argument instead of $time which is required by methods that need to modify time so |
573
|
3982
|
|
|
|
|
7335
|
# that we can update the keys to be consistent with the new time e.g. time_add(), time_subtract(), _reset_time_from_epoch() |
574
|
3982
|
|
|
|
|
9690
|
|
575
|
3982
|
|
|
|
|
7115
|
my ($pt, $time, $previous_pt) = (shift, shift); |
576
|
|
|
|
|
|
|
$previous_pt = pop if ref $_[-1] and $_[-1]->isa('Geo::TCX::Trackpoint'); |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
my $dt; |
579
|
46
|
|
|
46
|
|
88
|
if ( ref( $time ) and $time->isa('DateTime') ) { |
580
|
46
|
|
|
|
|
190
|
$dt = $time |
581
|
46
|
|
|
|
|
13121
|
} else { |
582
|
46
|
|
|
|
|
143
|
$pt->{Time} = $time; |
583
|
46
|
|
|
|
|
237
|
$dt = $pt->time_datetime |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
$pt->{Time} = _time_format($dt); |
587
|
4028
|
|
|
4028
|
|
8596
|
$pt->{_time_iso8601} = _time_format($dt); |
588
|
4028
|
100
|
|
|
|
8426
|
$pt->{_time_local} = _time_format($dt, local => 1); |
589
|
3933
|
50
|
|
|
|
11596
|
$pt->{_time_epoch} = $dt->epoch; |
590
|
|
|
|
|
|
|
|
591
|
4028
|
|
|
|
|
6503
|
if ( ! exists $pt->{_time_elapsed} ) { # i.e. immutable here |
592
|
4028
|
|
|
|
|
10703
|
if ( $previous_pt ) { |
593
|
4028
|
|
|
|
|
5676
|
$pt->{_time_elapsed} = $pt->{_time_epoch} - $previous_pt->{_time_epoch} |
594
|
|
|
|
|
|
|
} else { $pt->{_time_elapsed} = undef } |
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
return 1 |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
my $dt = shift; |
600
|
|
|
|
|
|
|
# !! TODO: check that ref is not a Garmin Object (croack that function is not a class method) |
601
|
|
|
|
|
|
|
my %opts = @_; |
602
|
|
|
|
|
|
|
if ($opts{'local'}) { |
603
|
|
|
|
|
|
|
$dt->set_formatter( $Formatter ); # see pattern in $Formatter |
604
|
4887
|
|
|
4887
|
|
10177
|
$dt->set_time_zone( $LocalTZ ) |
605
|
4887
|
100
|
66
|
|
|
19093
|
} else { |
606
|
|
|
|
|
|
|
$dt->set_formatter( $formatter_xsd ) |
607
|
4887
|
|
|
|
|
7220
|
} |
608
|
4887
|
100
|
66
|
|
|
14658
|
return $dt->stringify |
609
|
905
|
|
|
|
|
1529
|
} |
610
|
|
|
|
|
|
|
|
611
|
3982
|
|
|
|
|
6364
|
# Expects a decimal-number or integer and sets the C<DistanceMeters> field for the trackpoint and returns true |
612
|
3982
|
|
|
|
|
8246
|
# . if the _distance_elapsed key for the point is not already defined and another trackpoint object is also provided, |
613
|
|
|
|
|
|
|
# e.g. the previous trackpoint, it will also set it (number of meters from that previous point) |
614
|
|
|
|
|
|
|
|
615
|
4887
|
|
|
|
|
1829208
|
my ($pt, $meters, $previous_pt) = shift; |
616
|
4887
|
|
|
|
|
1045268
|
$previous_pt = pop if ref $_[-1] and $_[-1]->isa('Geo::TCX::Trackpoint'); |
617
|
4887
|
|
|
|
|
1004818
|
$meters = shift; |
618
|
4887
|
|
|
|
|
1089816
|
|
619
|
|
|
|
|
|
|
my $meters_formatted; |
620
|
4887
|
100
|
|
|
|
46086
|
$meters_formatted = sprintf("%.3f", $meters) if defined $meters; |
621
|
4028
|
100
|
|
|
|
8636
|
|
622
|
|
|
|
|
|
|
$pt->{DistanceMeters} = $meters_formatted; |
623
|
3933
|
|
|
|
|
8991
|
|
|
95
|
|
|
|
|
270
|
|
624
|
|
|
|
|
|
|
if ( ! exists $pt->{_distance_elapsed} ) { # i.e. immutable here |
625
|
4887
|
|
|
|
|
20054
|
if ( $previous_pt ) { |
626
|
|
|
|
|
|
|
my $dist_elapsed = $pt->DistanceMeters - $previous_pt->DistanceMeters; |
627
|
|
|
|
|
|
|
$pt->{_distance_elapsed} = sprintf("%.3f", $dist_elapsed) |
628
|
|
|
|
|
|
|
} else { $pt->{_distance_elapsed} = $meters_formatted } |
629
|
14661
|
|
|
14661
|
|
23902
|
} |
630
|
|
|
|
|
|
|
return 1 |
631
|
14661
|
|
|
|
|
28368
|
} |
632
|
14661
|
100
|
|
|
|
33635
|
|
633
|
4887
|
|
|
|
|
14862
|
} |
634
|
4887
|
|
|
|
|
220850
|
|
635
|
|
|
|
|
|
|
=head1 EXAMPLES |
636
|
9774
|
|
|
|
|
25378
|
|
637
|
|
|
|
|
|
|
Coming soon. |
638
|
14661
|
|
|
|
|
514726
|
|
639
|
|
|
|
|
|
|
=head1 AUTHOR |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Patrick Joly |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=head1 VERSION |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
1.02 |
646
|
5572
|
|
|
5572
|
|
9901
|
|
647
|
5572
|
100
|
66
|
|
|
21527
|
=head1 SEE ALSO |
648
|
5572
|
|
|
|
|
8671
|
|
649
|
|
|
|
|
|
|
perl(1). |
650
|
5572
|
|
|
|
|
6948
|
|
651
|
5572
|
50
|
|
|
|
39968
|
=cut |
652
|
|
|
|
|
|
|
|
653
|
5572
|
|
|
|
|
10083
|
1; |
654
|
|
|
|
|
|
|
|
655
|
5572
|
100
|
|
|
|
11340
|
|
656
|
4028
|
100
|
|
|
|
7770
|
A trackpoint string looks like: |
657
|
3933
|
|
|
|
|
8448
|
|
658
|
3933
|
|
|
|
|
19921
|
<Time>2014-08-11T10:55:26Z</Time><Position><LatitudeDegrees>45.293131</LatitudeDegrees><LongitudeDegrees>-72.650505</LongitudeDegrees></Position><AltitudeMeters>368.591</AltitudeMeters><DistanceMeters>3844.748</DistanceMeters><HeartRateBpm><Value>128</Value></HeartRateBpm> |