/online/index.py
Python | 136 lines | 119 code | 14 blank | 3 comment | 22 complexity | f6cd8ca75b3085bc764cfb9404a96393 MD5 | raw file
1#!/usr/bin/env python 2# vim: set fileencoding=utf-8 noexpandtab ts=4 sw=4 : 3 4from google.appengine.ext import webapp 5from google.appengine.ext.webapp.util import run_wsgi_app 6from google.appengine.ext.webapp import template 7from google.appengine.runtime.apiproxy_errors import OverQuotaError 8 9from django.utils import simplejson as json 10 11import urllib2 12import logging 13 14from gpxplot import parse_gpx_data,google_chart_url,var_dist,var_ele 15 16max_gpx_size = 1048576 17 18class GPXSizeError (Exception): 19 pass 20 21class NoAltitudeData (Exception): 22 pass 23 24def plot_on_request(request): 25 "Process POST request with GPX data. Return a URL of the plot." 26 imperial=request.get('imperial') 27 if imperial == 'on': 28 metric=False 29 else: 30 metric=True 31 logging.debug('metric='+str(metric)) 32 try: 33 url=request.get("gpxurl") 34 if url: # fetch GPX data 35 logging.debug('fetching GPX from '+url) 36 reader=urllib2.urlopen(url) 37 gpxsize = int(reader.headers["Content-Length"]) 38 logging.debug('gpxsize=%d' % gpxsize) 39 if gpxsize > max_gpx_size: 40 raise GPXSizeError("File is too large") 41 gpxdata=reader.read() 42 else: 43 logging.debug('using submitted GPX data') 44 gpxdata=request.get("gpxfile") 45 gpxsize=len(gpxdata) 46 logging.debug('gpxsize=%d' % gpxsize) 47 if gpxsize > max_gpx_size: 48 raise GPXSizeError("File is too large") 49 logging.debug('gpxdata='+gpxdata[:320]) 50 except Exception, e: 51 logging.debug(unicode(e)) 52 raise e 53 if len(gpxdata) == 0: 54 raise Exception("There is no GPX data to plot!") 55 # reduce number of points gradually, to fit URL length 56 npoints=700 57 url=None 58 while not url: 59 try: 60 trk=parse_gpx_data(gpxdata,npoints=npoints) 61 y=var_ele 62 max_ele=max([max([p[y] for p in s]) for s in trk if len(s) > 0]) 63 min_ele=min([min([p[y] for p in s]) for s in trk if len(s) > 0]) 64 if abs(max_ele) < 1e-3 and abs(min_ele) < 1e-3: 65 msg = 'File does not contain altitude data ' \ 66 + 'or it is flat sea level. Nothing to plot.' 67 raise NoAltitudeData(msg) 68 url=google_chart_url(trk,var_dist,var_ele,metric=metric) 69 except OverflowError, e: 70 npoints -= 100 71 if npoints <= 0: 72 raise e 73 return url 74 75class MainPage(webapp.RequestHandler): 76 def get(self): 77 content={'title':'Visualize GPX profile online', 78 'form':True,'bg':'#efefff'} 79 self.response.out.write(template.render('index.html',content)) 80 81 def post(self): 82 content={'title':'Elevation??istance profile','form':False, 'bg':'#fff'} 83 try: 84 url=plot_on_request(self.request) 85 content['imgsrc']=url 86 except GPXSizeError, e: 87 msg = 'File is too large to be processed on gpxplot.appspot.com. ' + \ 88 '<br/>Reduce file size with gpsbabel or plot it offline with a ' + \ 89 'stand-alone version of gpxplot.' 90 content['error'] = msg 91 logging.error(e) 92 except NoAltitudeData, e: 93 content['error'] = e.message 94 logging.error(e) 95 except OverQuotaError, e: 96 msg = 'Application exceeded free quota. Try again later.' 97 content['error'] = msg 98 logging.error(e) 99 except Exception, e: 100 msg = 'Your GPX track cannot be processed. Sorry.' 101 msg += '<br/>'+unicode(e) 102 content['error'] = msg 103 content['bugreportme'] = True 104 logging.error(e) 105 self.response.out.write(template.render('index.html',content)) 106 107class ApiHandler(webapp.RequestHandler): 108 "Return a URL of the image in a file." 109 def get(self): 110 return self.post() 111 def post(self): 112 try: 113 url=plot_on_request(self.request) 114 format=self.request.get("output","json") 115 if format == "json": 116 self.response.headers['Content-Type']='application/json' 117 self.response.out.write(json.dumps({'url':url})) 118 elif format == "png": 119 self.redirect(url) 120 else: 121 raise Exception("Output format not supported.") 122 except Exception, e: 123 self.response.set_status(400,message='Exception: '+unicode(e)) 124 125application = webapp.WSGIApplication( 126 [('/', MainPage), 127 (r'/api/0.1/plot',ApiHandler), 128 (r'/api/0.1.1/plot',ApiHandler), 129 (r'/api/0.1.2/plot',ApiHandler), 130 ], debug=True) 131 132def main(): 133 run_wsgi_app(application) 134 135if __name__ == "__main__": 136 main()