2 * Copyright (C) 2009-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://blog.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * Represents a point in the track or a waypoint.
31 public string name
= null;
32 /* Position, in radians and degrees, 1000 means not set */
33 public double lat
= 1000;
34 public double lon
= 1000;
35 public double lat_dec
= 1000;
36 public double lon_dec
= 1000;
37 /* The distance from start of track. (only if part of track */
38 public double distance
=0;
40 public double elevation
;
43 /* The speed (only if part of track */
44 public double speed
= 0;
47 private time_t utime
= 0;
49 /* Sets the poistion in degrees, automagically calculates radians */
50 public void set_position(double lat
, double lon
)
54 this
.lat
= (2*GLib
.Math
.PI
*lat
)/360.0;
55 this
.lon
= (2*GLib
.Math
.PI
*lon
)/360.0;
57 /* Get the unix time. (calculated on first request, then cached ) */
58 public time_t
get_time()
60 if(this
.time
== null) return 0;
64 ta
.strptime(this
.time
, "%FT%T%z");
65 this
.utime
= ta
.mktime();
71 * This class represents a Track in a gpx file.
72 * The tracks contains the points connecting everything together.
73 * Info like total distance, average speed, moving speed/time etc are available.
75 public class Track
: GLib
.Object
{
77 public string name
= null;
79 /* usefull info gathered during walking the list */
80 public double total_distance
= 0.0;
81 public double max_speed
= 0;
82 public List
<Point
> points
= null;
84 private Point? last
= null;
85 /* To get bounding box for view */
86 public Point top
= null;
87 public Point bottom
= null;
89 public void add_point (Point point
)
93 var distance
= calculate_distance(last
, point
);
94 this
.total_distance
+= distance
;
95 point
.distance
= this
.total_distance
;
98 if(last
.time
!= null && point
.time
!= null) {
99 point
.speed
= calculate_point_to_point_speed(last
, point
);
100 if((calculate_point_to_point_speed(this
.points
.first().data
, point
)*4) < point
.speed
)
101 point
.speed
= last
.speed
;
102 var avg
= point
.speed
;
103 if(avg
> this
.max_speed
) this
.max_speed
= avg
;
106 /* Update the 2 bounding box points */
107 if(top
== null || top
.lat_dec
== 1000 || top
.lat_dec
> point
.lat_dec
) {
108 if(top
== null) top
= new
Point();
109 top
.lat_dec
= point
.lat_dec
;
111 if(top
== null || top
.lon_dec
== 1000 || top
.lon_dec
> point
.lon_dec
) {
112 if(top
== null) top
= new
Point();
113 top
.lon_dec
= point
.lon_dec
;
115 if(bottom
== null || bottom
.lat_dec
== 1000 || bottom
.lat_dec
< point
.lat_dec
) {
116 if(bottom
== null) bottom
= new
Point();
117 bottom
.lat_dec
= point
.lat_dec
;
119 if(bottom
== null || bottom
.lon_dec
== 1000 || bottom
.lon_dec
< point
.lon_dec
) {
120 if(bottom
== null) bottom
= new
Point();
121 bottom
.lon_dec
= point
.lon_dec
;
125 points
.append(point
);
131 * Calculate the speed of the full track
134 public double get_track_average()
136 weak List
<Point ?
> first
= this
.points
.first();
137 weak List
<Point ?
> last
= this
.points
.last();
138 if(first
!= null && last
!= null) {
139 return this
.calculate_point_to_point_speed(first
.data
, last
.data
);
145 * Calculate the average speed between Point a and Point b on the track
147 public double calculate_point_to_point_speed(Point a
, Point b
)
149 var dist
= (b
.distance
- a
.distance
);
150 if(a
.time
== null || b
.time
== null) return 0;
151 var ta
= a
.get_time();
152 var tb
= b
.get_time();
153 if((tb
-ta
) == 0) return 0;
154 return dist
/((tb
-ta
)/(60.0*60.0));
156 public time_t
get_total_time()
159 weak List
<Point?
> na
= this
.points
.first();
160 weak List
<Point?
> nb
= this
.points
.last();
161 if(na
== null || nb
== null) return 0;
164 var time
= b
.get_time()-a
.get_time();
168 * Try not to calculate time that we "stopped" in average
170 public double calculate_moving_average(out time_t moving_time
)
175 weak List
<Point?
> iter
= this
.points
.first();
176 if(iter
== null) return 0;
178 while((iter
= iter
.next
)!= null)
181 if( (b
.distance
-a
.distance
) > 0.007){
183 time
+= (b
.get_time()-a
.get_time());
184 distance
+= b
.distance
-a
.distance
;
189 moving_time
= (time_t
)time
;
190 return distance
/(time
/(60.0*60.0));
193 /* Calculate distance between point a and point b using great circular distance method
194 * Elevation is not taken into account.
196 private double calculate_distance(Point a
, Point b
)
199 if(a
.lat
== b
.lat
&& a
.lon
== b
.lon
) return 0;
200 retv
= 6378.7 * Math
.acos(
201 Math
.sin(a
.lat
) * Math
.sin(b
.lat
) +
202 Math
.cos(a
.lat
) * Math
.cos(b
.lat
) * Math
.cos(b
.lon
- a
.lon
)
204 if(GLib
.Math
.isnan(retv
) == 1)
214 * This is the top level class representing the gpx file it self.
215 * This contains a list of tracks and waypoings.
217 public class File
: GLib
.Object
220 public string filename
= null;
221 /* A gpx file can contain multiple tracks, this supports it */
222 public GLib
.List
<Gpx
.Track
> tracks
= null;
223 /* A gpx file can also contains a list of waypoints */
224 public GLib
.List
<Gpx
.Point
> waypoints
= null;
226 public File (string filename
)
228 this
.filename
= filename
;
229 /* Start parsing the xml file */
230 Xml
.Doc doc
= Xml
.Parser
.parse_file(this
.filename
);
233 var node
= doc
.get_root_element();
236 node
= node
->children
;
238 /* Get the track element */
239 if(node
->name
== "trk")
241 /* Create new track here */
242 Gpx
.Track track
= new Gpx
.Track();
244 var trkseg
= node
->children
;
245 /* iterretate over track segments */
246 while(trkseg
!= null){
247 if(trkseg
->name
== "trkseg") {
248 var point
= trkseg
->children
;
251 if(point
->name
== "trkpt")
253 var lat
= point
->get_prop("lat");
254 var lon
= point
->get_prop("lon");
255 if(lat
!= null && lon
!= null)
257 Point p
= new
Point();
258 double flat
= lat
.to_double();
259 double flon
= lon
.to_double();
260 p
.set_position(flat
, flon
);
261 var info
= point
->children
;
262 while(info
!= null) {
264 if(info
->name
== "ele") {
265 var content
= info
->get_content();
267 p
.elevation
= content
.to_double();
268 }else if (info
->name
== "time") {
269 p
.time
= info
->get_content();
276 GLib
.message("Failed to get point: %s\n", point
->name
);
282 if(trkseg
->name
== "name"){
283 if(track
.name
== null){
284 track
.name
= trkseg
->get_content();
285 stdout
.printf("Track name: '%s'\n", track
.name
);
288 GLib
.warning("Track name allready set: %s\n", track
.name
);
292 trkseg
= trkseg
->next
;
294 this
.tracks
.append(track
);
296 else if (node
->name
== "wpt")
298 var lat
= node
->get_prop("lat");
299 var lon
= node
->get_prop("lon");
300 if(lat
!= null && lon
!= null)
302 Point p
= new
Point();
303 double flat
= lat
.to_double();
304 double flon
= lon
.to_double();
305 p
.set_position(flat
, flon
);
306 var info
= node
->children
;
307 while(info
!= null) {
308 if(info
->name
== "name"){
310 p
.name
= info
->get_content();
311 stdout
.printf("Point name: '%s'\n", p
.name
);
314 GLib
.warning("Point name allready set: %s\n", p
.name
);
319 this
.waypoints
.append(p
);
326 /* Todo add error trower here http://www.vala-project.org/doc/vala-draft/errors.html#exceptionsexamples*/
327 GLib
.message("Failed to open file");