source: subversion/applications/utils/amr2wav/parseGPX.py @ 17248

Last change on this file since 17248 was 14494, checked in by gjones, 11 years ago

initial import

File size: 14.5 KB
Line 
1#!/usr/bin/python
2#----------------------------------------------------------------------------
3# Library for handling GPX data
4#
5# Based on ParseOsm.py from pyrender
6#
7# Handles:
8#   * Interesting nodes, with tags (store as list)
9#   * Ways, with tags (store as list)
10#   * Position of all nodes (store as hash)
11#----------------------------------------------------------------------------
12# Copyright 2008, Oliver White (initial ParseOSM.py code)
13#           2009, Graham Jones (modified to parse and analyse GPX files)
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program.  If not, see <http://www.gnu.org/licenses/>.
27#
28# HISTORY:
29#     Feb/March 2009  GJ  ORIGIAL VERSION (part of wherewasi.py)
30#     10Apr2009       GJ   Added support for audio waypoints to allow it to be
31#                          used in gpxamr2wav
32#
33#---------------------------------------------------------------------------
34import sys
35import os
36from xml.sax import make_parser, handler
37import xml
38from time import *
39from geo import *
40
41class parseGPX(handler.ContentHandler):
42  def __init__(self, filename,opt_debug,opt_verbose):
43    """Load a GPX XML file into memory"""
44    self.debug = opt_debug
45    self.verbose = opt_verbose
46    self.inTrk = 0
47    self.inTrkseg = 0
48    self.inTrkpt = 0
49    self.inWpt = 0
50    self.inLink = 0
51    self.inName = 0
52    self.inTime = 0
53    self.inEle = 0
54    self.trk = []
55    self.wayPts = []
56    self.trkseg = []
57    self.fname = "no file loaded"
58    if(filename != None):
59      self.loadGPX(filename)
60
61  def loadGPX(self, filename):
62    """Load a GPX XML file into memory"""
63    #print "loadGPX()"
64    if(not os.path.exists(filename)):
65      print "Error - File %s does not exist." % filename
66      return
67    try:
68      parser = make_parser()
69      parser.setContentHandler(self)
70      parser.parse(filename)
71      self.fname = filename
72    except xml.sax._exceptions.SAXParseException:
73      print "Error loading %s" % filename
74   
75
76  def startElement(self, name, attrs):
77    """Handle XML elements"""
78   # print "name=%s, atrr=%s" % (name,attrs)
79    if name == "trk":
80      #print "Starting track"
81      self.inTrk = 1
82    if name == "trkseg":
83      #print "Starting track segment"
84      self.inTrkseg = 1
85    if name == "trkpt":
86      self.tags = { \
87        'lat':float(attrs.get('lat')), 
88        'lon':float(attrs.get('lon'))}
89      self.inTrkpt = 1
90    if name == "time":
91      if self.inTrkpt or self.inWpt:
92        self.inTime = 1
93      else:
94        print "startElement: ERROR time element found outside of a waypoint or trackpoint???"
95      self.timeStr = ''
96    if name == "ele":
97      if self.inTrkpt:
98        self.inEle = 1
99      self.eleStr = ''
100    if name == "wpt":
101      self.tags = { \
102        'lat':float(attrs.get('lat')), 
103        'lon':float(attrs.get('lon'))}
104      self.inWpt = 1
105    if name == "link":
106      if self.inWpt:
107        self.inLink = 1
108      else:
109        print("startElement: ERROR - Link element found outside of a waypoint??")
110      self.linkStr = ''
111    if name == "name":
112      if self.inWpt:
113        self.inName = 1
114      else:
115        print("startElement: ERROR - Name element found outside of a waypoint??")
116      self.nameStr = ''
117     
118  def characters(self, content):
119    if(self.inTime):
120      self.timeStr = self.timeStr + content
121    if(self.inEle):
122      self.eleStr = self.eleStr + content
123    if(self.inLink):
124      self.linkStr = self.linkStr + content
125    if(self.inName):
126      self.nameStr = self.nameStr + content
127 
128  def endElement(self, name):
129    if name == 'time':
130      if(self.inTime):
131        self.inTime = 0
132        self.tags['t'] = mktime(strptime(self.timeStr[0:-1], "%Y-%m-%dT%H:%M:%S"))
133        self.tags['time'] = self.timeStr
134    if name == 'ele':
135      if (self.inEle):
136        self.inEle = 0
137        self.tags['ele'] = float(self.eleStr)
138    if name == 'trkpt':
139      self.trkseg .append(self.tags)
140      self.inTrkpt = 0
141    if name =='trkseg':
142      #print "Ending Track Segment"
143      self.trk.append(self.trkseg)
144      self.trkseg = []
145      self.inTrkseg = 0
146    if name == 'trk':
147      #print "Ending Track"
148      self.inTrk = 0
149    if name == 'link':
150      self.inLink = 0
151      self.tags['link'] = self.linkStr
152    if name == 'name':
153      self.inName = 0
154      self.tags['name'] = self.nameStr
155    if name == 'wpt':
156      self.inWpt = 0
157      self.wayPts.append(self.tags)
158
159
160  def getTrackAnalysis(self,s_seg,s_pt,e_seg,e_pt):
161    """Analyse the track between the segment s_seg, point s_pt,
162    and segment e_seg, point e_pt and calculate a number of statistics
163    which are returned as a tuple.
164    Specifying the end point e_pt as -1 will result in the whole
165    segment being analysed.
166    GJ 19 Feb 2009  ORIGINAL VERSION
167    """
168
169    if self.debug: 
170      print "getTrackAnalysis: s_seg=%d, s_pt=%d, e_seg=%d, e_pt=%d\n" % \
171          (s_seg,s_pt,e_seg,e_pt)
172    if not self.segNoValid(s_seg):
173      print "segment numbers must lie in the range 0 to %d.\n" % (self.getNumTrkSeg()-1)
174      return -1
175    if not self.segNoValid(e_seg):
176      print "segment numbers must lie in the range 0 to %d.\n" % (self.getNumTrkSeg()-1)
177      return -1
178     
179    if not self.ptNoValid(s_seg,s_pt):
180      print "Start point out of range - must be in range %d to %d\n" % \
181          (-1,self.getNumPts(s_seg)-1)
182      return -1
183    if e_pt == -1:
184      e_pt = self.getNumPts(e_seg)-1
185    if not self.ptNoValid(e_seg,e_pt):
186      print "Start point out of range - must be in range %d to %d\n" % \
187          (-1,self.getNumPts(e_seg)-1)
188      return -1
189
190    if e_seg==-1:
191        e_seg = self.getNumTrkSeg()-1
192        if self.debug: print "Default e_seg used - set to %d" % e_seg
193    else:
194        if self.debug: print "e_seg=%s" % e_seg
195
196
197    if e_pt==-1:
198        e_pt = self.getNumPts(e_seg)-1
199        if self.debug: print "Default e_pt used - set to %d" \
200                % e_pt
201    else:
202        if self.debug: print "e_pt=%d" % e_pt
203
204
205
206    results = {}
207    npts = 0
208    climb = 0  # total climb (m)
209    dist = 0   # total distance (km)
210    time = 0   # total time (sec)
211    maxSpeed = 0 # maximum speed (km/hr)
212    maxSpeedSeg = 0 # segment containing maximum speed
213    maxSpeedPt = 0 # point within segment of maximum speed
214    maxTime = 0    # latest time (sec)
215    maxTimeSeg = 0 # segment containing latest time
216    maxTimePt = 0  # point within segment of latest time
217    trkpt = self.getTrkPt(s_seg,s_pt)
218    minTime = trkpt['t'] # earliest time (sec)
219    minTimeSeg = s_seg   # segment containing earliest time
220    minTimePt = s_pt     # point within segment of earliest time
221    prev = -999
222
223    for seg in range(s_seg,e_seg+1):
224      # Start and end points for current segment
225      seg_s_pt = -1
226      seg_e_pt = -1
227      if (seg==s_seg):
228        seg_s_pt = s_pt
229      else:
230        seg_s_pt = 0
231
232      if (seg==e_seg):
233        seg_e_pt = e_pt
234      else:
235        seg_e_pt = self.getNumPts(seg)-1
236
237
238      if self.debug:
239        print "getTrackAnalysis(): Analysing segment %d, from point %d to point %d." % (seg,seg_s_pt,seg_e_pt)
240
241      for pt in range(seg_s_pt,seg_e_pt+1):
242        trkpt = self.getTrkPt(seg,pt)
243        npts = npts + 1
244        #print "seg=%d, pt=%d trkpt=%s" % (seg,pt,trkpt)
245        # Check max and min time
246        if trkpt['t']>maxTime:
247          maxTime = trkpt['t']
248          maxTimeSeg = seg
249          maxTimePt = pt
250        if trkpt['t']<minTime:
251          minTime = trkpt['t']
252          minTimeSeg = seg
253          minTimePt = pt
254
255        if prev != -999:
256          # calculate distance travelled
257          #a = (trkpt['lat'],trkpt['lon'])
258          #b = (prev['lat'],prev['lon'])
259          #ddist = distance(a,b)
260          ddist = distance(trkpt['lat'],trkpt['lon'],prev['lat'],prev['lon'])
261          dist = dist + ddist
262
263          # calculate increase in elevation
264          if trkpt['ele']>prev['ele']:
265            climb = climb + trkpt['ele']-prev['ele']
266            #print "climb=%f" % climb
267
268          # calculate time difference between trkpt and prev
269          dtime = trkpt['t'] - prev['t']
270          time = time + dtime
271          #print "time=%f" % time
272
273          # calculate speed
274          speed = ddist/(dtime/3600.0)
275          if speed>maxSpeed:
276            maxSpeed = speed
277            maxSpeedSeg = seg
278            maxSpeedPt = pt
279        else:
280          pass
281          #print "prev=-999 - skipping first point"
282        prev = trkpt
283    results['npts'] = npts
284    results['climb']=climb
285    results['dist'] = dist
286    results['time'] = time
287    if (time!=0):
288      results['avSpeed'] = dist/(time/3600.)
289    else:
290      results['avSpeed'] = 0.0
291    results['maxSpeed']=maxSpeed
292    results['maxSpeedSeg'] = maxSpeedSeg
293    results['maxSpeedPt'] = maxSpeedPt
294    results['minTime'] = minTime
295    results['minTimeSeg'] = minTimeSeg
296    results['minTimePt'] = minTimePt
297    results['maxTime'] = maxTime
298    results['maxTimeSeg'] = maxTimeSeg
299    results['maxTimePt'] = maxTimePt
300    #print results
301    return results
302
303
304  def getProfileData(self,s_seg,s_pt,e_seg,e_pt):
305    """Get the elevation profile of the track between the segment
306    s_seg, point s_pt,
307    and segment e_seg, point e_pt
308    Specifying the end point e_pt as -1 will result in the whole
309    segment being analysed.
310    GJ 20 Feb 2009  ORIGINAL VERSION
311    """
312
313    #print "getProfileData: s_seg=%d, s_pt=%d, e_seg=%d, e_pt=%d\n" % \
314    #    (s_seg,s_pt,e_seg,e_pt)
315    if not self.segNoValid(s_seg):
316      print "segment numbers must lie in the range 0 to %d.\n" % (self.getNumTrkSeg()-1)
317      return -1
318    if not self.segNoValid(e_seg):
319      print "segment numbers must lie in the range 0 to %d.\n" % (self.getNumTrkSeg()-1)
320      return -1
321     
322    if not self.ptNoValid(s_seg,s_pt):
323      print "Start point out of range - must be in range %d to %d\n" % \
324          (0,self.getNumPts(s_seg)-1)
325      return -1
326    if e_pt == -1:
327      e_pt = self.getNumPts(e_seg)-1
328    if not self.ptNoValid(e_seg,e_pt):
329      print "Start point out of range - must be in range %d to %d\n" % \
330          (0,self.getNumPts(e_seg)-1)
331      return -1
332
333    if e_seg==-1:
334        e_seg = self.getNumTrkSeg()-1
335        if self.debug: print "Default e_seg used - set to %d" % e_seg
336    else:
337        if self.debug: print "e_seg=%s" % e_seg
338
339
340    if e_pt==-1:
341        e_pt = self.getNumPts(e_seg)-1
342        if self.debug: print "Default e_pt used - set to %d" \
343                % e_pt
344    else:
345        if self.debug: print "e_pt=%d" % e_pt
346
347
348    results = []
349    trkpt = self.getTrkPt(s_seg,s_pt)
350    s_time = trkpt['t']
351    prev = -999
352    dist = 0
353    speed = 0
354
355    for seg in range(s_seg,e_seg+1):
356      # Start and end points for current segment
357      seg_s_pt = -1
358      seg_e_pt = -1
359      if (seg==s_seg):
360        seg_s_pt = s_pt
361      else:
362        seg_s_pt = 0
363
364      if (seg==e_seg):
365        seg_e_pt = e_pt
366      else:
367        seg_e_pt = self.getNumPts(seg)-1
368
369
370      #print "getProfileData(): Analysing segment %d, from point %d to point %d." % (seg,seg_s_pt,seg_e_pt)
371
372
373      for pt in range(seg_s_pt,seg_e_pt+1):
374        trkpt = self.getTrkPt(seg,pt)
375
376        if prev != -999:
377          # calculate distance travelled
378          #a = (trkpt['lat'],trkpt['lon'])
379          #b = (prev['lat'],prev['lon'])
380          #ddist = distance(a,b)
381          ddist = distance(trkpt['lat'],trkpt['lon'],prev['lat'],prev['lon'])
382          dist = dist + ddist
383          dtime = trkpt['t'] - prev['t']
384          speed = ddist / (dtime/3600.)
385        else:
386          pass
387          #print "prev=-999 - skipping first point"
388        resrec = (trkpt['t'],trkpt['t']-s_time,dist,trkpt['ele'],speed)
389        results.append(resrec)
390        prev = trkpt
391    return results
392
393
394
395
396  def getNumTrkSeg(self):
397    """Returns the number of track segments read from the GPX file."""
398    return len(self.trk)
399
400  def getNumPts(self,segno):
401    """Returns the number of points in a given track segment."""
402    if not self.segNoValid(segno):
403      print "getNumPts(): Error: segment numbers must lie in the range 0 to %d.\n" \
404          % (self.getNumTrkSeg()-1)
405      return -1
406    else:
407      return len(self.trk[segno])
408
409  def segNoValid(self,segno):
410    """ Returns true if the segment number provided is within the allowable
411    range, or false if it is not"""
412    if segno < -1 or segno> len(self.trk)-1:
413      print "segNoValid(): Error: segment number %d invalid: segment numbers must lie in the range 0 to %d.\n" \
414          % (segno,len(self.trk)-1)
415      return False
416    else:
417      return True
418
419  def ptNoValid(self,segno,ptno):
420    """Returns true if the point number specified is within the allowable range
421    for segment number segno, otherwise returns false."""
422    if not self.segNoValid(segno):
423      print "ptNoValid(): Error: Segment Number %d invalid: segment numbers must lie in the range -1 to %d.\n" \
424          % (segno,self.getNumTrkSeg()-1)
425      return False
426    else:
427      if ptno<-1 or ptno > self.getNumPts(segno)-1:
428        print "ptNoValid(): Error: Point numbers for segment %d  must lie in the range -1 to %d.\n" \
429          % (self.getNumPts(segno)-1)
430        return False
431      else:
432        return True
433
434  def getTrkPt(self,segno,ptno):
435    """Returns the track point, tuple of 'lat', 'lon', 'ele' and 'time'
436    for the specified segment number and point number."""
437    if self.ptNoValid(segno,ptno):
438      return self.trk[segno][ptno]
439    else:
440      return -1
441
442  def getPos(self,segno,ptno):
443    """Returns the latitude and longitude of the point number ptno in
444    segment number segno."""
445   
446    if self.ptNoValid(segno,ptno):
447      pt = self.trk[segno][ptno]
448      pos = (pt['lat'],pt['lon'])
449      return pos
450    else:
451      return -1
452
453  def getEle(self,segno,ptno):
454    """Returns the elevation of the point number ptno in
455    segment number segno."""
456   
457    if self.ptNoValid(segno,ptno):
458      pt = self.trk[segno][ptno]
459      ele = pt['ele']
460      return ele
461    else:
462      return -1
463
464
465if __name__ == "__main__":
466  track = parseGPX(sys.argv[1])
467
468  numseg = len(track.trk)
469  print "There are %d segments in the Track" % numseg
470  for segno in range(0,numseg):
471    print "Segment %d Contains %d points" % (segno,len(track.trk[segno]))
472
473#  for pos in track.samples:
474#    print "%s: (%f,%f), ele=%s\n" % (pos['t'],pos['lat'],pos['lon'],pos['ele'])
475
476
Note: See TracBrowser for help on using the repository browser.