Add option to hide tracks
[gpx-viewer.git] / src / gpx-parser.vala
blob0702f7222e23b0b2692320488935af4654a328ef
1 /* Gpx Viewer
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.
20 using Gtk;
21 using GLib;
22 using Xml;
25 namespace Gpx {
26 /**
27 * Represents a point in the track or a waypoint.
29 public class Point {
30 /* Waypoint name */
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;
39 /* Elevation */
40 public double elevation;
41 /* Time */
42 public string time;
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)
52 this.lat_dec = lat;
53 this.lon_dec = 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;
61 if(this.utime > 0)
62 return this.utime;
63 Time ta = Time();
64 ta.strptime(this.time, "%FT%T%z");
65 this.utime = ta.mktime();
66 return utime;
70 /**
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 {
76 /* make a property */
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)
91 if(last != null)
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);
126 last = point;
129 /* Private api */
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);
141 return 0;
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()
158 Point a, b;
159 weak List<Point?> na = this.points.first();
160 weak List<Point?> nb = this.points.last();
161 if(na == null || nb == null) return 0;
162 a = na.data;
163 b = nb.data;
164 var time = b.get_time()-a.get_time();
165 return time;
168 * Try not to calculate time that we "stopped" in average
170 public double calculate_moving_average(out time_t moving_time)
172 double time = 1;
173 double distance = 0;
174 moving_time = 0;
175 weak List<Point?> iter = this.points.first();
176 if(iter == null) return 0;
177 Point a = iter.data;
178 while((iter = iter.next)!= null)
180 Point b = iter.data;
181 if( (b.distance-a.distance) > 0.007){
182 a = iter.prev.data;
183 time += (b.get_time()-a.get_time());
184 distance += b.distance-a.distance;
186 a = b;
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)
198 double retv =0;
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)
206 return 0;
208 return retv;
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
219 /* The filename */
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);
231 if(doc != null)
233 var node = doc.get_root_element();
234 if(node != null)
236 node = node->children;
237 while(node != null){
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;
249 while(point != null)
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) {
263 /* height */
264 if(info->name == "ele") {
265 var content = info->get_content();
266 if(content != null)
267 p.elevation = content.to_double();
268 }else if (info->name == "time") {
269 p.time = info->get_content();
271 info = info->next;
274 track.add_point(p);
275 }else{
276 GLib.message("Failed to get point: %s\n", point->name);
279 point = point->next;
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);
287 else{
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"){
309 if(p.name == null){
310 p.name = info->get_content();
311 stdout.printf("Point name: '%s'\n", p.name);
313 else{
314 GLib.warning("Point name allready set: %s\n", p.name);
317 info = info->next;
319 this.waypoints.append(p);
322 node = node->next;
325 }else{
326 /* Todo add error trower here http://www.vala-project.org/doc/vala-draft/errors.html#exceptionsexamples*/
327 GLib.message("Failed to open file");