File Coverage

blib/lib/GPX/PlotElevation.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package GPX::PlotElevation;
2             # ABSTRACT: create elevation graphs from GPX files
3              
4              
5             =head1 NAME
6            
7             GPX::PlotElevation - a perl module for creating elevation graphs
8            
9             =head1 SYNOPSIS
10              
11             use GPX::PlotElevation;
12            
13             my $plot = GPX::PlotElevation->new(
14             'gpxfile' => $ARGV[0],
15             'output' => 'transalp-2009.png',
16             'width' => 40,
17             'height' => 10,
18             );
19             $plot->calc();
20             $plot->plot();
21              
22             =head1 FUNCTIONS
23              
24             =cut
25              
26 1     1   1045 use strict;
  1         2  
  1         38  
27 1     1   6 use warnings;
  1         3  
  1         46  
28              
29             our $VERSION = '1.01'; # VERSION
30              
31 1     1   3135 use IO::File;
  1         25003  
  1         616  
32 1     1   1217 use Geo::Gpx;
  0            
  0            
33             use Geo::Distance;
34             use Chart::Gnuplot;
35              
36             sub new {
37             my $invocant = shift();
38             my $class = ref($invocant) || $invocant;
39              
40             my $self = {
41             gpxfile => undef,
42             gpxfh => undef,
43             gpx => undef,
44             title => "Hoehenprofil",
45             xlabel => 'Entfernung [km]',
46             ylabel => 'Hoehe [m]',
47             output => undef,
48             fileformat => 'png',
49             width => 10,
50             height => 7,
51             formula => 'hsin',
52             _points => [],
53             _wp_points => [],
54             _total_dist => 0,
55             _gpx => undef,
56             _geodist => Geo::Distance->new(),
57             @_
58             };
59             bless($self, $class);
60              
61             $self->{'_geodist'}->formula($self->{'formula'});
62             $self->_load_gpx();
63             $self->fileformat($self->{'fileformat'});
64              
65             return($self);
66             }
67              
68             sub fileformat {
69             my $self = shift();
70             my $fileformat = shift();
71              
72             if($fileformat !~ m/^(png)$/) {
73             die("unsupported output fileformat: ".$fileformat);
74             }
75             $self->{'fileformat'} = $fileformat;
76             }
77              
78             sub _load_gpx {
79             my $self = shift();
80             my $infh;
81              
82             if(defined $self->{'gpx'}) {
83             $self->{'_gpx'} = Geo::Gpx->new( xml => $self->{'gpx'});
84             return();
85             }
86             if(defined $self->{'gpxfile'}) {
87             $infh = IO::File->new("<".$self->{'gpxfile'});
88             $self->{'_gpx'} = Geo::Gpx->new( input => $infh );
89             $infh->close();
90             return();
91             }
92             if(defined $self->{'gpxfh'}) {
93             $self->{'_gpx'} = Geo::Gpx->new( input => $self->{'gpxfh'} );
94             return();
95             }
96              
97             die('No GPX input given.');
98             }
99              
100             =head2 calc()
101              
102             Calculate the distance between points in tracks and assig
103             the waypoints to trackpoints.
104              
105             =cut
106              
107             sub calc {
108             my $self = shift();
109              
110             $self->_calculate_distances();
111             $self->_assign_waypoints();
112             }
113              
114             sub _calculate_distances {
115             my $self = shift();
116             my $tracks = $self->{'_gpx'}->tracks();
117             my $dist;
118             my $lastpoint = undef;
119              
120             foreach my $track (@$tracks) {
121             my $segments = $track->{'segments'};
122             foreach my $segment (@$segments) {
123             my $points = $segment->{'points'};
124             foreach my $point (@$points) {
125             if(defined $lastpoint) {
126             $dist = $self->{'_geodist'}->distance('kilometer',
127             $lastpoint->{'lon'},$lastpoint->{'lat'} =>
128             $point->{'lon'},$point->{'lat'});
129             $self->{'_total_dist'} += $dist;
130             $point->{'dist'} = $self->{'_total_dist'}."";
131             $point->{'dist'} =~ s/^(\d+\.\d{2})\d+$/$1/;
132             } else {
133             $point->{'dist'} = 0;
134             }
135             $lastpoint = $point;
136             push(@{$self->{'_points'}}, $point);
137             }
138             }
139             }
140             }
141              
142             # find nearest track point for each waypoint
143             sub _assign_waypoints {
144             my $self = shift();
145             my $waypoints = $self->{'_gpx'}->waypoints();
146             my $dist;
147             my $nearest;
148             my $nearest_dist;
149              
150             foreach my $wp (@$waypoints) {
151             $nearest = undef;
152             $nearest_dist = undef;
153             foreach my $point (@{$self->{'_points'}}) {
154             if( $point->{'lat'} > ( $wp->{'lat'} - 0.05 ) &&
155             $point->{'lat'} < ( $wp->{'lat'} + 0.05 ) &&
156             $point->{'lon'} > ( $wp->{'lon'} - 0.05 ) &&
157             $point->{'lon'} < ( $wp->{'lon'} + 0.05 ) ) {
158             $dist = $self->{'_geodist'}->distance('kilometer',
159             $point->{'lon'},$point->{'lat'} => $wp->{'lon'},$wp->{'lat'});
160             if(!defined $nearest) {
161             $nearest = $point;
162             $nearest_dist = $dist;
163             next;
164             }
165             if($dist < $nearest_dist) {
166             $nearest = $point;
167             $nearest_dist = $dist;
168             }
169             }
170             }
171             if(defined $nearest) {
172             if(!defined $nearest->{'waypoints'}) {
173             $nearest->{'waypoints'} = [];
174             }
175             push(@{$nearest->{'waypoints'}}, $wp);
176             push(@{$self->{'_wp_points'}}, $nearest);
177             }
178             }
179             }
180              
181             =head2 print_datfile()
182              
183             Output a datfile for futher usage. eg. with GNUplot to STDOUT.
184              
185             =cut
186              
187             sub print_datfile {
188             my $self = shift();
189              
190             foreach my $point (@{$self->{'_points'}}) {
191             print $point->{'dist'}." ".$point->{'ele'};
192             if(defined $point->{'waypoints'}) {
193             print ' "'.join(',', map {$_->{'name'}} @{$point->{'waypoints'}}).'"';
194             }
195             print "\n";
196             }
197             }
198              
199             =head2 plot()
200              
201             Generate the plot and write to the output file.
202              
203             =cut
204              
205             sub plot {
206             my $self = shift();
207             my $fileformat = $self->{'fileformat'};
208             my $last_point = $self->{'_points'}->[@{$self->{'_points'}} - 1 ];
209             my $chart = Chart::Gnuplot->new(
210             title => $self->{'title'},
211             output => $self->{'output'},
212             xlabel => $self->{'xlabel'},
213             ylabel => $self->{'ylabel'},
214             imagesize => $self->{'width'}.','.$self->{'height'},
215             grid => ' xtics ytics x2tics',
216             xtics => 'on',
217             x2tics => 'on',
218             xrange => [0, $last_point->{'dist'}],
219             x2range => [0, $last_point->{'dist'}],
220             );
221             $chart->convert($fileformat);
222              
223             my $ele_data = Chart::Gnuplot::DataSet->new(
224             title => 'Hoehenprofil',
225             style => 'lines',
226             xdata => [ map {$_->{'dist'}} @{$self->{'_points'}}],
227             ydata => [ map {$_->{'ele'}} @{$self->{'_points'}}],
228             );
229             foreach my $point (@{$self->{'_wp_points'}}) {
230             $chart->label(
231             text => join(',', map {$_->{'name'}} @{$point->{'waypoints'}})." (".int($point->{'ele'}).'m)',
232             rotate => 45,
233             position => $point->{'dist'}.','.$point->{'ele'},
234             offset => "1,1",
235             );
236             }
237             my $wp_data = Chart::Gnuplot::DataSet->new(
238             title => 'Wegpunkte',
239             style => 'points',
240             xdata => [ map {$_->{'dist'}} @{$self->{'_wp_points'}}],
241             ydata => [ map {$_->{'ele'}} @{$self->{'_wp_points'}}],
242             using => '1:2:xticlabels(1)',
243             pointsize => 3,
244             pointtype => 3,
245             );
246              
247             $chart->plot2d($ele_data, $wp_data);
248             }
249              
250             =head1 SEE ALSO
251              
252             plot-gpx
253              
254             =head1 COPYRIGHT & LICENSE
255              
256             Copyright 2009 Markus Benning
257              
258             This program is free software; you can redistribute it and/or modify it
259             under the same terms as Perl itself.
260              
261             =cut
262              
263             1;