+++ /dev/null
-#!/usr/bin/python3.4
-import logging
-
-from pylons import request, response, session, url, tmpl_context as c
-from pylons.controllers.util import abort, redirect
-from pylons.decorators.rest import restrict
-
-from wradmin.lib.base import BaseController, render
-from wradmin.lib.wrgpx import parse_wrgpx, etree, height_profile
-from pylons.i18n.translation import _, set_lang
-
-log = logging.getLogger(__name__)
-
-class WrgpxtoolController(BaseController):
-
- def index(self):
- c.validated = False
- return render('wrgpxtool.html')
-
- @restrict('POST')
- def upload(self):
- gpxfile = request.POST['gpxfile']
- if gpxfile != '':
- try:
- set_lang('de')
- (gpx, hints) = parse_wrgpx(string = gpxfile.value)
- c.validation_exception = None
- c.validation_hints = hints
- c.validation_ok = len([hint for hint in hints if hint.level > 1]) == 0
- session['wrgpx'] = gpx
- if c.validation_ok: session['flash'] = "Die Datei '%s' ist eine gültige GPX 1.1 Datei und entspricht zusätzlich den Winterrodeln Anforderungen (WRGPX). :-)" % gpxfile.filename
- else: session['flash'] = " Die Datei '%s' ist zwar eine gültige GPX 1.1 Datei, entspricht aber nicht den zusätzlichen Winterrodeln-Anforderungen. :-(" % gpxfile.filename
- except etree.XMLSyntaxError as e:
- c.validation_exception = e
- c.validation_hints = []
- c.validation_ok = False
- session['flash'] = "Die Datei '%s' ist keine gültige GPX 1.1 Datei. :-(" % gpxfile.filename
- c.validated = True
- c.filename = gpxfile.filename
- else:
- c.validated = False
- session['flash'] = "Bitte eine GPX-Datei angeben!"
- session.save()
- return render('wrgpxtool.html')
-
-
- def height_profile(self):
- response.content_type = 'image/png'
- response.charset = None
- if 'wrgpx' in session:
- wrgpx = session['wrgpx']
- s = height_profile(wrgpx)
- return s
+++ /dev/null
-#!/usr/bin/python3.4
-"""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 mapnik2 as mapnik
-import datetime
-import os
-from lxml import etree
-import numpy as np
-import matplotlib
-matplotlib.use('Agg')
-import matplotlib.pyplot as plt
-import io
-from pylons.i18n.translation import _
-
-
-
-
-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
- def __repr__(self):
- return "Waypoint %s" % str(self.name)
-
-
-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)
- def __repr__(self):
- return "Track %s" % str(self.name)
-
-
-class WrGpx:
- def __init__(self):
- self.metadata = MetaData()
- self.waypoints = []
- self.tracks = []
- def __repr__(self):
- return "WrGpx (%d waypoints; %d tracks)" % (len(self.waypoints), len(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 __repr__(self):
- return "Hint: %s" % self.message
-
-
-def parse_wrgpx(filename=None, string=None):
- """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 or not an GPX 1.1 file, an etree.XMLSyntaxError exception is raised."""
- hints = []
- wrgpx = WrGpx()
-
- ns = '{http://www.topografix.com/GPX/1/1}'
-
- def parse_wpt(element):
- waypoint = Waypoint()
- waypoint.lat = float(element.attrib['lat'])
- waypoint.lon = float(element.attrib['lon'])
- for el in element:
- if el.tag == ns+'ele': waypoint.ele = float(el.text)
- elif el.tag == ns+'name': waypoint.name = el.text
- elif el.tag == ns+'desc': waypoint.desc = el.text
- elif el.tag == ns+'link': waypoint.link = el.attrib['href']
- elif el.tag == ns+'type': waypoint.type = el.text
- return waypoint
-
- schema = etree.XMLSchema(file=os.path.dirname(os.path.realpath(__file__)) + os.sep + 'gpx_1_1.xsd')
- parser = etree.XMLParser(schema=schema)
- if not string is None: root = etree.fromstring(string, parser)
- else:
- # xml = etree.parse(open(filename, 'rb'), parser)
- xml = etree.parse(filename, parser)
- root = xml.getroot()
- assert root.tag == ns + 'gpx' # Nothing else should be possible after validation
-
- 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':
- wrgpx.metadata.author_email = e.attrib['id'] + '@' + e.attrib['domain']
- 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
- elif el.tag == ns+'link': wrgpx.metadata.link = el.attrib['href']
- elif el.tag == ns+'time':
- wrgpx.metadata.time = datetime.datetime.strptime(el.text, '%Y-%m-%dT%H:%M:%SZ') # '2008-11-10T20:44:52Z'
- elif el.tag == ns+'bounds':
- wrgpx.metadata.minlat = float(el.attrib['minlat'])
- wrgpx.metadata.minlon = float(el.attrib['minlon'])
- wrgpx.metadata.maxlat = float(el.attrib['maxlat'])
- wrgpx.metadata.maxlon = float(el.attrib['maxlon'])
- elif el.tag == ns+'extensions': hints.append(Hint("XML element extensions within XML element metadata is not used by WRGPX.", 2))
-
- # Waypoints
- elif element.tag == ns+'wpt':
- waypoint = parse_wpt(element)
- wrgpx.waypoints.append(waypoint)
- if waypoint.ele is None: hints.append(Hint(_("Elevation of waypoint '%s' should be given.") % str(waypoint), 1))
- if waypoint.name is None: hints.append(Hint(_("Name of waypoint '%s' must be given.") % str(waypoint), 2))
- if waypoint.type is None: hints.append(Hint(_("Type of waypoint '%s' must be given (Gasthaus, Bushaltestelle, ...)") % str(waypoint), 2))
- if not waypoint.type in ['Gasthaus', 'Parkplatz', 'Bushaltestelle']: hints.append(Hint(_("Type of the waypoint '%s' has to be one of (Gasthaus, Bushaltestelle, ...)") % str(waypoint), 2))
-
- # Routes
- elif element.tag == ns+'rte':
- hints.append(Hint(_("GPX file has a 'rte' element. This is not used in WRGPX."), 2))
-
- # 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+'desc': track.desc = el.text
- elif el.tag == ns+'link': track.link = el.attrib['href']
- elif el.tag == ns+'type': track.type = el.text
- elif el.tag == ns+'trkseg':
- no_ele = 0
- for e in el:
- if e.tag == ns+'trkpt':
- trkpt = parse_wpt(e)
- track.points.append((trkpt.lat, trkpt.lon, trkpt.ele))
- if trkpt.ele is None: no_ele += 1
- elif e.tag == ns+'extensions': hints.append(Hint(_("XML element extensions within XML element trkpt is not used by WRGPX."), 2))
- if no_ele > 0: hints.append(Hint(_("%d of %d track points have no elevation (%s).") % (no_ele, len(track.points), str(track)), 1))
- if len(track.points) == 0: hints.append(Hint(_("track '%s' is empty.") % str(track), 2))
- 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."), 2))
-
- if not wrgpx_has_metadata: hints.append(Hint(_("GPX file has a no metadata (like author, ...)."), 2))
- return (wrgpx, hints)
-
-
-def height_profile(wrgpx):
- "Create a elevation profile for the wrgpx class. Raises a RuntimError in case of an error, otherwise returns the PNG file as string."
- proj_utm32 = mapnik.Projection("+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
- bahntracks = [t for t in wrgpx.tracks if t.type=='Rodelbahn' and len(t.points) > 0]
- if len(bahntracks) == 0: raise RuntimeError(_("There is no 'Rodelbahn' type track in the GPX file."))
-
- # Descending order and check
- for track in bahntracks:
- if track.points[0][2] is None or track.points[-1][2] is None: raise RuntimeError(_('Track has no elevation data'))
- if track.points[0][2] < track.points[-1][2]: track.points[:] = track.points[::-1]
- bahntracks.sort(lambda x, y: -1 if x.points[0][2] > y.points[0][2] else 1)
- points = []
- for track in bahntracks: points.extend(track.points)
-
- points = np.array(points)
- for i in range(len(points)):
- c = mapnik.Coord(points[i][1], points[i][0])
- c = c.forward(proj_utm32)
- points[i][0] = c.y
- points[i][1] = c.x
-
- # Calculate profile
- s = np.sqrt(np.diff(points[:, 0]) ** 2 + np.diff(points[:, 1]) ** 2)
- x = np.hstack((0, np.cumsum(s)))
- h = points[:, 2]
-
- # Plot
- axxmin = 0
- axxmax = x[-1]
- axymin = np.floor_divide(min(h)-50, 100)*100
- axymax = np.floor_divide(max(h)+150, 100)*100
- f = plt.figure()
- plt.fill(np.hstack((x, x[-1], x[0])), np.hstack((h, 0, 0)), fc='#014e9a', ec='none')
- plt.plot(x, h, color='black')
- plt.axis([axxmin, axxmax, axymin, axymax])
- plt.grid()
- plt.xlabel('Strecke in m [%d m Gesamtstrecke]' % round(x[-1], -1))
- plt.ylabel('Höhe in m [%d m Differenz]' % round(h[0]-h[-1]))
- plt.title('%s - Höhenprofil' % wrgpx.metadata.name)
-
- width_px = 500
- imgdata = io.StringIO()
- f.savefig(imgdata, dpi=width_px/plt.gcf().get_figwidth())
- s = imgdata.getvalue()
- imgdata.close()
- return s
+++ /dev/null
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
-<xi:include href="master.html" />
-
-<head>
- <title>WRGPX-Werkzeug</title>
-</head>
-
-<body>
-
-<h2>WRGPX-Werkzeug</h2>
-
-<p><strong>Achtung: Da das WRGPX Format noch in Arbeit ist, ist diese Seite natürlich auch noch nicht fertig!</strong></p>
-
-<py:if test="not c.validated">
-<p>WRGPX steht für "Winterrodeln GPX". Hier können Sie eine .gpx Datei auf ihre Gültigkeit untersuchen.</p>
-
-${h.form(h.url(controller='wrgpxtool', action='upload'), multipart=True)}
-GPX-Datei zum Untersuchen: ${h.file('gpxfile')}<br/>
-${h.submit('submit', 'Raufladen')}
-${h.end_form()}
-</py:if>
-
-<py:if test="c.validated">
-<h3>Ergebnis der Validierung</h3>
-
-<py:if test="c.validation_exception is None">
-<p>Die Datei <em>${c.filename}</em> ist eine gültige GPX 1.1 Datei. :-)</p>
-
-<py:if test="not c.validation_ok">
-<p>Die Datei entspricht allerdings nicht den zusätzlichen Anforderungen von Winterrodeln an GPX Dateien (WRGPX):</p>
-<ul>
- <li py:for="hint in c.validation_hints">${hint.message} (level ${hint.level})</li>
-</ul>
-</py:if>
-
-<py:if test="c.validation_ok">
-<p>Sie entspricht außerdem den zusätzlichen Anforderungen von Winterrodeln an GPX Dateien (WRGPX).</p>
-<img src="${h.url(controller='wrgpxtool', action='height_profile')}"/>
-
-<py:if test="len(c.validation_hints) > 0">
-<p>Trotzdem könnte die Datei noch verbessert werden:</p>
-<ul>
- <li py:for="hint in c.validation_hints">${hint.message}</li>
-</ul>
-</py:if>
-</py:if>
-
-</py:if>
-
-<py:if test="not c.validation_exception is None">
-<p>Die Datei <em>${c.filename}</em> ist <strong>keine</strong> gülte GPX 1.1 Datei. :-(<br/>
-Der validierende XML-Parser lieferte folgende Fehlermeldung:</p>
-<p><em>${str(c.validation_exception)}</em></p>
-</py:if>
-
-<p><a href="${h.url(controller='wrgpxtool', action='index')}">Andere/geänderte GPX-Datei untersuchen</a></p>
-</py:if>
-
-
-<h3>Links</h3>
-<ul>
- <li><a href="http://www.topografix.com/gpx.asp">Informationen über das GPX-Format</a></li>
- <li><a href="http://www.topografix.com/GPX/1/1/">Kommentierte GPX 1.1 Definition</a></li>
- <li><a href="http://www.winterrodeln.org/trac/wiki/WrGpx">Zusätzliche Anforderungen an das GPX-Format, um es bei Winterrodeln verwenden zu können</a></li>
-</ul>
-
-</body>
-</html>