2017-10-24 20:05:47 +02:00
|
|
|
import hashlib
|
2017-10-20 16:47:53 +02:00
|
|
|
import os
|
|
|
|
|
|
|
|
from django.conf import settings
|
2017-10-24 18:12:46 +02:00
|
|
|
from django.core.cache import cache
|
2017-10-24 13:59:42 +02:00
|
|
|
from django.core.exceptions import PermissionDenied
|
2017-10-24 20:05:47 +02:00
|
|
|
from django.core.signing import b64_encode
|
2017-10-20 16:21:25 +02:00
|
|
|
from django.http import Http404, HttpResponse, HttpResponseNotModified
|
2017-10-24 13:59:42 +02:00
|
|
|
from django.shortcuts import get_object_or_404
|
2017-10-24 21:40:10 +02:00
|
|
|
from django.views.decorators.http import etag
|
2017-10-10 14:39:11 +02:00
|
|
|
from shapely.geometry import box
|
|
|
|
|
2017-10-24 13:59:42 +02:00
|
|
|
from c3nav.mapdata.cache import MapHistory
|
2017-10-24 21:03:56 +02:00
|
|
|
from c3nav.mapdata.middleware import no_language
|
2017-10-20 16:21:25 +02:00
|
|
|
from c3nav.mapdata.models import Level, MapUpdate, Source
|
2017-10-24 22:45:57 +02:00
|
|
|
from c3nav.mapdata.models.access import AccessPermission
|
2017-10-24 18:12:46 +02:00
|
|
|
from c3nav.mapdata.render.base import get_render_level_ids
|
2017-10-19 17:20:55 +02:00
|
|
|
from c3nav.mapdata.render.svg import SVGRenderer
|
2017-10-10 14:39:11 +02:00
|
|
|
|
|
|
|
|
2017-10-24 21:03:56 +02:00
|
|
|
@no_language()
|
2017-10-10 14:39:11 +02:00
|
|
|
def tile(request, level, zoom, x, y, format):
|
|
|
|
zoom = int(zoom)
|
|
|
|
if not (0 <= zoom <= 10):
|
|
|
|
raise Http404
|
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# calculate bounds
|
2017-10-10 14:39:11 +02:00
|
|
|
x, y = int(x), int(y)
|
|
|
|
size = 256/2**zoom
|
|
|
|
minx = size * x
|
2017-10-10 19:31:51 +02:00
|
|
|
miny = size * (-y-1)
|
2017-10-10 14:39:11 +02:00
|
|
|
maxx = minx + size
|
|
|
|
maxy = miny + size
|
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# error 404 if tiles is out of bounds
|
|
|
|
bounds = Source.max_bounds()
|
2017-10-10 14:39:11 +02:00
|
|
|
if not box(bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0]).intersects(box(minx, miny, maxx, maxy)):
|
|
|
|
raise Http404
|
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# is this a valid level?
|
|
|
|
cache_key = MapUpdate.current_cache_key()
|
|
|
|
level = int(level)
|
|
|
|
if level not in get_render_level_ids(cache_key):
|
|
|
|
raise Http404
|
2017-10-19 17:20:55 +02:00
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# init renderer
|
2017-10-24 22:45:57 +02:00
|
|
|
renderer = SVGRenderer(level, miny, minx, maxy, maxx, scale=2**zoom,
|
|
|
|
access_permissions=AccessPermission.get_for_request(request))
|
2017-10-24 18:12:46 +02:00
|
|
|
tile_cache_key = renderer.cache_key
|
|
|
|
update_cache_key = renderer.update_cache_key
|
2017-10-20 16:21:25 +02:00
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# check browser cache
|
2017-10-24 21:39:45 +02:00
|
|
|
etag = '"'+b64_encode(hashlib.sha256(
|
2017-10-24 20:05:47 +02:00
|
|
|
('%d-%d-%d-%d:%s:%s' % (level, zoom, x, y, tile_cache_key, settings.SECRET_TILE_KEY)).encode()
|
2017-10-24 23:24:32 +02:00
|
|
|
).digest()).decode()+'"'
|
2017-10-20 16:21:25 +02:00
|
|
|
if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
|
|
|
|
if if_none_match == etag:
|
|
|
|
return HttpResponseNotModified()
|
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
data = None
|
|
|
|
tile_dirname, last_update_filename, tile_filename, tile_cache_update_cache_key = '', '', '', ''
|
2017-10-20 16:47:53 +02:00
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
# get tile cache last update
|
|
|
|
if settings.CACHE_TILES:
|
|
|
|
tile_dirname = os.path.sep.join((settings.TILES_ROOT, str(level), str(zoom), str(x), str(y)))
|
|
|
|
last_update_filename = os.path.join(tile_dirname, 'last_update')
|
|
|
|
tile_filename = os.path.join(tile_dirname, renderer.access_cache_key+'.'+format)
|
|
|
|
|
|
|
|
# get tile cache last update
|
|
|
|
tile_cache_update_cache_key = 'mapdata:tile-cache-update:%d-%d-%d-%d' % (level, zoom, x, y)
|
|
|
|
tile_cache_update = cache.get(tile_cache_update_cache_key, None)
|
|
|
|
if tile_cache_update is None:
|
|
|
|
try:
|
|
|
|
with open(last_update_filename) as f:
|
|
|
|
tile_cache_update = f.read()
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if tile_cache_update != update_cache_key:
|
|
|
|
os.system('rm -rf '+os.path.join(tile_dirname, '*'))
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
with open(tile_filename, 'rb') as f:
|
|
|
|
data = f.read()
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
2017-10-20 16:47:53 +02:00
|
|
|
|
|
|
|
content_type = 'image/svg+xml' if format == 'svg' else 'image/png'
|
|
|
|
|
2017-10-24 18:12:46 +02:00
|
|
|
if data is None:
|
2017-10-20 16:47:53 +02:00
|
|
|
svg = renderer.render()
|
|
|
|
if format == 'svg':
|
|
|
|
data = svg.get_xml()
|
|
|
|
filemode = 'w'
|
|
|
|
elif format == 'png':
|
|
|
|
data = svg.get_png()
|
|
|
|
filemode = 'wb'
|
|
|
|
else:
|
|
|
|
raise ValueError
|
2017-10-19 17:20:55 +02:00
|
|
|
|
2017-10-20 16:47:53 +02:00
|
|
|
if settings.CACHE_TILES:
|
2017-10-24 18:12:46 +02:00
|
|
|
os.makedirs(tile_dirname, exist_ok=True)
|
|
|
|
with open(tile_filename, filemode) as f:
|
2017-10-20 16:47:53 +02:00
|
|
|
f.write(data)
|
2017-10-24 18:12:46 +02:00
|
|
|
with open(last_update_filename, 'w') as f:
|
|
|
|
f.write(update_cache_key)
|
|
|
|
cache.get(tile_cache_update_cache_key, update_cache_key, 60)
|
2017-10-10 14:39:11 +02:00
|
|
|
|
2017-10-20 16:47:53 +02:00
|
|
|
response = HttpResponse(data, content_type)
|
2017-10-20 16:21:25 +02:00
|
|
|
response['ETag'] = etag
|
|
|
|
response['Cache-Control'] = 'no-cache'
|
2017-10-24 21:16:47 +02:00
|
|
|
response['X-Access-Restrictions'] = ', '.join(str(s) for s in renderer.unlocked_access_restrictions) or '0'
|
2017-10-20 16:21:25 +02:00
|
|
|
|
2017-10-10 14:39:11 +02:00
|
|
|
return response
|
2017-10-24 13:59:42 +02:00
|
|
|
|
|
|
|
|
2017-10-24 21:40:10 +02:00
|
|
|
@etag(lambda *args, **kwargs: 'abc')
|
2017-10-24 21:06:42 +02:00
|
|
|
@no_language()
|
2017-10-24 13:59:42 +02:00
|
|
|
def history(request, level, mode, format):
|
|
|
|
if not request.user.is_superuser:
|
|
|
|
raise PermissionDenied
|
|
|
|
level = get_object_or_404(Level, pk=level)
|
|
|
|
|
2017-10-24 15:23:41 +02:00
|
|
|
if mode == 'render' and level.on_top_of_id is not None:
|
2017-10-24 13:59:42 +02:00
|
|
|
raise Http404
|
|
|
|
|
2017-10-24 14:46:35 +02:00
|
|
|
history = MapHistory.open_level(level.pk, mode)
|
2017-10-24 13:59:42 +02:00
|
|
|
if format == 'png':
|
|
|
|
response = HttpResponse(content_type='image/png')
|
|
|
|
history.to_image().save(response, format='PNG')
|
|
|
|
elif format == 'data':
|
|
|
|
response = HttpResponse(content_type='application/octet-stream')
|
|
|
|
history.write(response)
|
|
|
|
else:
|
|
|
|
raise ValueError
|
2017-10-24 21:40:10 +02:00
|
|
|
response['Cache-Control'] = 'no-cache'
|
2017-10-24 13:59:42 +02:00
|
|
|
return response
|