/online/index.py

http://gpxplot.googlecode.com/ · Python · 136 lines · 119 code · 14 blank · 3 comment · 32 complexity · f6cd8ca75b3085bc764cfb9404a96393 MD5 · raw file

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