app.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import uuid
  2. import os
  3. import zipfile
  4. import urllib.request
  5. import gzip
  6. from io import BytesIO
  7. import time
  8. from flask import Flask
  9. from flask import render_template
  10. from flask import request
  11. from terrain_gen import add_offset
  12. # Directory of this file
  13. this_path = os.path.dirname(os.path.realpath(__file__))
  14. # Where the user requested tile are stored
  15. output_path = os.path.join(this_path, '..', 'userRequestTerrain')
  16. # Where the data database is
  17. tile_path = os.path.join(this_path, '..', 'data', 'tiles')
  18. # The output folder for all gzipped terrain requests
  19. app = Flask(__name__, static_url_path='/terrain', static_folder=output_path,)
  20. def clamp(n, smallest, largest):
  21. return max(smallest, min(n, largest))
  22. def getDatFile(lat, lon):
  23. '''Get file'''
  24. if lat < 0:
  25. NS = 'S'
  26. else:
  27. NS = 'N'
  28. if lon < 0:
  29. EW = 'W'
  30. else:
  31. EW = 'E'
  32. return "%c%02u%c%03u.DAT.gz" % (NS, min(abs(int(lat)), 99), EW, min(abs(int(lon)), 999))
  33. def compressFiles(fileList, uuidkey):
  34. # create a zip file comprised of dat.gz tiles
  35. zipthis = os.path.join(output_path, uuidkey + '.zip')
  36. # create output dirs if needed
  37. try:
  38. os.makedirs(output_path)
  39. except OSError:
  40. pass
  41. try:
  42. os.makedirs(tile_path)
  43. except OSError:
  44. pass
  45. try:
  46. with zipfile.ZipFile(zipthis, 'w') as terrain_zip:
  47. for fn in fileList:
  48. if not os.path.exists(fn):
  49. #download if required
  50. print("Downloading " + os.path.basename(fn))
  51. g = urllib.request.urlopen('https://terrain.ardupilot.org/data/tiles/' +
  52. os.path.basename(fn))
  53. print("Downloaded " + os.path.basename(fn))
  54. with open(fn, 'b+w') as f:
  55. f.write(g.read())
  56. # need to decompress file and pass to zip
  57. with gzip.open(fn, 'r') as f_in:
  58. myio = BytesIO(f_in.read())
  59. print("Decomp " + os.path.basename(fn))
  60. # and add file to zip
  61. terrain_zip.writestr(os.path.basename(fn)[:-3], myio.read(),
  62. compress_type=zipfile.ZIP_DEFLATED)
  63. except Exception as ex:
  64. print("Unexpected error: {0}".format(ex))
  65. return False
  66. return True
  67. @app.route('/')
  68. def index():
  69. return render_template('index.html')
  70. @app.route('/generate', methods=['GET', 'POST'])
  71. def generate():
  72. if request.method == 'POST':
  73. # parse and sanitise the input
  74. try:
  75. # request.form['username']
  76. lat = float(request.form['lat'])
  77. lon = float(request.form['long'])
  78. radius = int(request.form['radius'])
  79. assert lat < 90
  80. assert lon < 180
  81. assert lat > -90
  82. assert lon > -180
  83. radius = clamp(radius, 1, 400)
  84. except:
  85. print("Bad data")
  86. return render_template('generate.html', error="Error with input")
  87. # UUID for this terrain generation
  88. uuidkey = str(uuid.uuid1())
  89. # Flag for if user wanted a tile outside +-60deg latitude
  90. outsideLat = None
  91. # get a list of files required to cover area
  92. filelist = []
  93. done = set()
  94. for dx in range(-radius, radius):
  95. for dy in range(-radius, radius):
  96. (lat2, lon2) = add_offset(lat*1e7, lon*1e7, dx*1000.0, dy*1000.0)
  97. lat_int = int(round(lat2 * 1.0e-7))
  98. lon_int = int(round(lon2 * 1.0e-7))
  99. tag = (lat_int, lon_int)
  100. if tag in done:
  101. continue
  102. done.add(tag)
  103. # make sure tile is inside the 60deg latitude limit
  104. if abs(lat_int) <= 60:
  105. filelist.append(tile_path, getDatFile(lat_int, lon_int)))
  106. else:
  107. outsideLat = True
  108. # make sure tile is inside the 60deg latitude limit
  109. if abs(lat_int) <= 60:
  110. filelist.append(os.path.join(tile_path, getDatFile(lat_int, lon_int)))
  111. else:
  112. outsideLat = True
  113. # remove duplicates
  114. filelist = list(dict.fromkeys(filelist))
  115. print(filelist)
  116. #compress
  117. success = compressFiles(filelist, uuidkey)
  118. # as a cleanup, remove any generated terrain older than 24H
  119. for f in os.listdir(output_path):
  120. if os.stat(os.path.join(output_path, f)).st_mtime < time.time() - 24 * 60 * 60:
  121. print("Removing old file: " + str(os.path.join(output_path, f)))
  122. os.remove(os.path.join(output_path, f))
  123. if success:
  124. print("Generated " + "/terrain/" + uuidkey + ".zip")
  125. return render_template('generate.html', urlkey="/terrain/" + uuidkey + ".zip",
  126. uuidkey=uuidkey, outsideLat=outsideLat)
  127. else:
  128. print("Failed " + "/terrain/" + uuidkey + ".zip")
  129. return render_template('generate.html', error="Cannot generate terrain",
  130. uuidkey=uuidkey)
  131. else:
  132. print("Bad get")
  133. return render_template('generate.html', error="Need to use POST, not GET")
  134. if __name__ == "__main__":
  135. app.run()