--- /dev/null
+#!/usr/bin/python2.5
+# -*- coding: iso-8859-15 -*-
+"""Library to handle WRGPX files.
+A WRGPX file (WR stands for Winterrodeln) is a normal GPX file
+that follows several conventions so that it can be rendered to
+a map automatically."""
+
+import mapnik
+import datetime
+from xml.etree.cElementTree import parse
+
+
+class MetaData:
+ def __init__(self):
+ self.name = None # name as string or None
+ self.desc = None # description as string or None
+ self.author = None # author as string or None
+ self.author_email = None # email as string or None
+ self.copyright_year = None # year of copyright as int or None
+ self.license = None # link to license
+ self.link = None # link as string or None
+ self.time = None # datetime class or None
+ self.minlat = None # float coordinate or None
+ self.minlon = None # float coordinate or None
+ self.maxlat = None # float coordinate or None
+ self.maxlon = None # float coordinate or None
+
+
+class Waypoint:
+ def __init__(self):
+ self.lat = None # float coordinate or None
+ self.lon = None # float coordinate or None
+ self.ele = None # height as float value or None
+ self.name = None # name as string or None
+ self.type = None # type as string or None
+
+
+class Track:
+ def __init__(self):
+ self.name = None # name as string or None
+ self.type = None # type as string or None
+ self.points = [] # one point is a tuple (lat, lon, ele) or (lat, lon, None)
+
+
+class WrGpx:
+ def __init__(self):
+ self.metadata = MetaData()
+ self.waypoints = []
+ self.tracks = []
+
+
+class Hint:
+ def __init__(self, message, level, line_nr=None):
+ "level ... 0 informal information; 1 ... suggestion for improvement; 2 ... warning; 3 ... error"
+ self.message = message
+ self.level = level
+ self.line_nr = line_nr
+
+
+def parse_wrgpx(filename):
+ """Parses a WRGPX file and returns a tuple of a data structure representing the content of the GPX file
+ and a list of Hint classes. If the file is not well-formed, an exception is raised."""
+ hints = []
+ wrgpx = WrGpx()
+
+ ns = '{http://www.topografix.com/GPX/1/1}'
+
+ def parse_link(el):
+ if len(el.items()) == 0: hints.append(Hint("XML element link does not have an attribute 'href'.", 2))
+ elif len(el.items()) > 1: raise Exception("XML element link has more than one attribute (should only have 'href').")
+ elif el.items()[0][0] != 'href': raise Exception("XML attribute '%s' is not allowed within XML element link (only 'href' is allowed)." % el.items()[0][0])
+ else: return el.items()[0][1]
+ return None
+
+ def parse_wpt(element):
+ waypoint = Waypoint()
+ for k, v in element.items():
+ if k == 'lat': waypoint.lat = float(v)
+ elif k == 'lon': waypoint.lon = float(v)
+ else: raise Exception("Unexpected XML attribute '%s' of 'wpt'." % k)
+ for el in element:
+ if el.tag == ns+'ele': waypoint.ele = float(el.text)
+ elif el.tag == ns+'time': pass
+ elif el.tag == ns+'magvar': pass
+ elif el.tag == ns+'geoidheight': pass
+ elif el.tag == ns+'name': waypoint.name = el.text
+ elif el.tag == ns+'cmt': pass
+ elif el.tag == ns+'desc': waypoint.desc = el.text
+ elif el.tag == ns+'src': pass
+ elif el.tag == ns+'link': waypoint.link = parse_link(el)
+ elif el.tag == ns+'sym': pass
+ elif el.tag == ns+'type': waypoint.type = el.text
+ elif el.tag == ns+'fix': pass
+ elif el.tag == ns+'sat': pass
+ elif el.tag == ns+'hdop': pass
+ elif el.tag == ns+'vdop': pass
+ elif el.tag == ns+'pdop': pass
+ elif el.tag == ns+'ageofdgpsdata': pass
+ elif el.tag == ns+'dgpsid': pass
+ elif el.tag == ns+'extensions': pass
+ else: raise Exception("Unexpected XML element '%s' within '%s'." % (el.tag, element.tag))
+ return waypoint
+
+ xml = parse(open(filename, 'rb'))
+ root = xml.getroot()
+ if root.tag != ns + 'gpx':
+ raise Exception("XML root element '%s' has to be '%sgpx'!" % (root.tag, ns))
+
+ wrgpx_has_metadata = False
+ for element in root:
+ # Metadata
+ if element.tag == ns+'metadata':
+ wrgpx_has_metadata = True
+ for el in element:
+ if el.tag == ns+'name':
+ wrgpx.metadata.name = el.text
+ elif el.tag == ns+'desc':
+ wrgpx.metadata.desc = el.text
+ elif el.tag == ns+'author':
+ for e in el:
+ if e.tag == ns+'name': wrgpx.metadata.author = e.text
+ elif e.tag == ns+'email':
+ mail_id = None
+ mail_domain = None
+ for k, v in e.items():
+ if k == 'id': mail_id = v
+ elif k == 'domain': mail_domain = v
+ else: raise Exception("Unexpected XML attribute '%s' of email." % k)
+ if mail_id is None: raise Exception("Missing XML attribute 'id' of email.")
+ if mail_domain is None: raise Exception("Missing XML attribute 'domain' of email.")
+ wrgpx.metadata.author_email = mail_id + '@' + mail_domain
+ else: raise Exception("Unexpected XML element '%s' within author." % e.tag)
+ elif el.tag == ns+'copyright':
+ for e in el:
+ if e.tag == ns+'year': wrgpx.metadata.copyright_year = int(e.text)
+ elif e.tag == ns+'license': wrgpx.metadata.license = e.text
+ else: raise Exception("Unexpected XML element '%s' within copyright." % e.tag)
+ elif el.tag == ns+'link': wrgpx.metadata.link = parse_link(el)
+ elif el.tag == ns+'time':
+ wrgpx.metadata.time = datetime.datetime.strptime('2008-11-10T20:44:52Z', '%Y-%m-%dT%H:%M:%SZ')
+ elif el.tag == ns+'bounds':
+ for k, v in el.items():
+ if k == 'minlat': wrgpx.metadata.minlat = float(v)
+ elif k == 'minlon': wrgpx.metadata.minlon = float(v)
+ elif k == 'maxlat': wrgpx.metadata.maxlat = float(v)
+ elif k == 'maxlon': wrgpx.metadata.maxlon = float(v)
+ else: raise Exception("Unexpected XML attribute '%s' of bounds." % k)
+ if wrgpx.metadata.minlat is None or wrgpx.metadata.minlon is None or wrgpx.metadata.maxlat is None or wrgpx.metadata.maxlon is None:
+ raise Exception("Not all of the attributes 'minlat', 'minlon', 'maxlat', 'maxlon' present in XML element 'bounds'.")
+ elif el.tag == ns+'extensions':
+ hints.append(Hint("XML element extensions within XML element metadata is not used by WRGPX.", 1))
+ else: raise Exception("Unexpected XML element '%s' within metadata." % el.tag)
+
+ # Waypoints
+ elif element.tag == ns+'wpt':
+ waypoint = parse_wpt(element)
+ wrgpx.waypoints.append(waypoint)
+
+ # Routes
+ elif element.tag == ns+'rte':
+ hints.append(Hint("GPX file has a 'rte' element. This is not used in WRGPX.", 1))
+
+ # Tracks
+ elif element.tag == ns+'trk':
+ track = Track()
+ for el in element:
+ if el.tag == ns+'name': track.name = el.text
+ elif el.tag == ns+'cmt': pass
+ elif el.tag == ns+'desc': track.desc = el.text
+ elif el.tag == ns+'src': pass
+ elif el.tag == ns+'link': track.link = parse_link(el)
+ elif el.tag == ns+'number': pass
+ elif el.tag == ns+'type': track.type = el.text
+ elif el.tag == ns+'extensions': pass
+ elif el.tag == ns+'trkseg':
+ for e in el:
+ if e.tag == ns+'trkpt':
+ trkpt = parse_wpt(e)
+ track.points.append((trkpt.lat, trkpt.lon, trkpt.ele))
+ elif e.tag == ns+'extensions': pass
+ else: raise Exception("Unexpected XML element '%s' within 'trkseg'." % e.tag)
+ else: raise Exception("Unexpected XML element '%s' within wpt." % el.tag)
+ wrgpx.tracks.append(track)
+
+ # Extensions
+ elif element.tag == ns+'extensions':
+ hints.append(Hint("GPX file has a 'extensions' element. This is not used in WRGPX.", 1))
+
+ else: raise Exception("Unexpected XML element '%s'. It has to be one of '%s', '%s' or '%s'." % (element.tag, ns+'metadata', ns+'wpt', ns+'trk'))
+ if not wrgpx_has_metadata: hints.append(Hint("GPX file has a no metadata (like author, ...).", 2))
+ return (wrgpx, hints)