-
Notifications
You must be signed in to change notification settings - Fork 16
/
app.py
executable file
·202 lines (171 loc) · 6.53 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import uuid
import os
import sys
import zipfile
import urllib.request
import gzip
from io import BytesIO
import time
import math
from flask import Flask
from flask import render_template
from flask import request
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from werkzeug.middleware.proxy_fix import ProxyFix
from terrain_gen import add_offset
# Directory of this file
this_path = os.path.dirname(os.path.realpath(__file__))
# Where the user requested tile are stored
output_path = os.path.join(this_path, '..', 'userRequestTerrain')
# Where the tile database is
if "pytest" in sys.modules:
# If we're in test mode, use remote URL, not local filesystem
tile_path1 = os.path.join(this_path, '..', 'tilesdat1')
url_path1 = 'https://terrain.ardupilot.org/tilesdat1/'
tile_path3 = os.path.join(this_path, '..', 'tilesdat3')
url_path3 = 'https://terrain.ardupilot.org/tilesdat3/'
else:
tile_path3 = os.path.join('/mnt/terrain_data/data/tilesdat3')
tile_path1 = os.path.join('/mnt/terrain_data/data/tilesdat1')
url_path1 = None
url_path3 = None
app = Flask(__name__, static_url_path='/userRequestTerrain', static_folder=output_path,)
# for example if the request goes through one proxy
# before hitting your application server
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
def clamp(n, smallest, largest):
return max(smallest, min(n, largest))
def getDatFile(lat, lon):
'''Get file'''
if lat < 0:
NS = 'S'
else:
NS = 'N'
if lon < 0:
EW = 'W'
else:
EW = 'E'
return "%c%02u%c%03u.DAT.gz" % (NS, min(abs(int(lat)), 99), EW, min(abs(int(lon)), 999))
def compressFiles(fileList, uuidkey, version):
# create a zip file comprised of dat.gz tiles
zipthis = os.path.join(output_path, uuidkey + '.zip')
# create output dirs if needed
try:
os.makedirs(output_path)
except OSError:
pass
version = int(version)
if version == 1:
url_path = url_path1
try:
os.makedirs(tile_path1)
except OSError:
pass
elif version == 3:
url_path = url_path3
try:
os.makedirs(tile_path3)
except OSError:
pass
print("compressFiles: version=%u url_path=%s" % (version, url_path))
try:
with zipfile.ZipFile(zipthis, 'w') as terrain_zip:
for fn in fileList:
if not os.path.exists(fn) and url_path != None:
#download if required
print("Downloading " + os.path.basename(fn))
g = urllib.request.urlopen(url_path +
os.path.basename(fn))
print("Downloaded " + os.path.basename(fn))
with open(fn, 'b+w') as f:
f.write(g.read())
# need to decompress file and pass to zip
with gzip.open(fn, 'r') as f_in:
myio = BytesIO(f_in.read())
print("Decomp " + os.path.basename(fn))
# and add file to zip
terrain_zip.writestr(os.path.basename(fn)[:-3], myio.read(),
compress_type=zipfile.ZIP_DEFLATED)
except Exception as ex:
print("Unexpected error: {0}".format(ex))
return False
return True
@app.route('/')
def index():
return render_template('index.html')
@app.route('/generate', methods=['GET', 'POST'])
def generate():
if request.method == 'POST':
# parse and sanitise the input
try:
# request.form['username']
lat = float(request.form['lat'])
lon = float(request.form['long'])
radius = int(request.form['radius'])
version = int(request.form['version'])
assert lat < 90
assert lon < 180
assert lat > -90
assert lon > -180
assert version in [1, 3]
radius = clamp(radius, 1, 400)
except:
print("Bad data")
return render_template('generate.html', error="Error with input")
version = int(version)
print("Generate: %.9f %.9f %.3f version=%u" % (lat, lon, radius, version))
# UUID for this terrain generation
uuidkey = str(uuid.uuid1())
# Flag for if user wanted a tile outside +-84deg latitude
outsideLat = None
# get a list of files required to cover area
filelist = []
done = set()
format = "4.1"
if version == 1:
tile_path = tile_path1
else:
tile_path = tile_path3
for dx in range(-radius, radius):
for dy in range(-radius, radius):
(lat2, lon2) = add_offset(lat*1e7, lon*1e7, dx*1000.0, dy*1000.0, format)
lat_int = int(math.floor(lat2 * 1.0e-7))
lon_int = int(math.floor(lon2 * 1.0e-7))
tag = (lat_int, lon_int)
if tag in done:
continue
done.add(tag)
# make sure tile is inside the 84deg lat limit
if abs(lat_int) <= 84:
filelist.append(os.path.join(tile_path, getDatFile(lat_int, lon_int)))
else:
outsideLat = True
# remove duplicates
filelist = list(dict.fromkeys(filelist))
print(filelist)
#compress
success = compressFiles(filelist, uuidkey, version)
# as a cleanup, remove any generated terrain older than 24H
for f in os.listdir(output_path):
if os.stat(os.path.join(output_path, f)).st_mtime < time.time() - 24 * 60 * 60:
print("Removing old file: " + str(os.path.join(output_path, f)))
os.remove(os.path.join(output_path, f))
if success:
print("Generated " + "/terrain/" + uuidkey + ".zip")
return render_template('generate.html', urlkey="/userRequestTerrain/" + uuidkey + ".zip",
uuidkey=uuidkey, outsideLat=outsideLat)
else:
print("Failed " + "/terrain/" + uuidkey + ".zip")
return render_template('generate.html', error="Cannot generate terrain",
uuidkey=uuidkey)
else:
print("Bad get")
return render_template('generate.html', error="Need to use POST, not GET")
if __name__ == "__main__":
app.run()