From 4fed59c72393e7c0d0f8f02deb4aa78e866b108a Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Mon, 21 Oct 2019 05:28:34 +0300 Subject: [PATCH] Use sqltomvt function --- server.py | 80 +++++++------------------------------------------------ 1 file changed, 10 insertions(+), 70 deletions(-) diff --git a/server.py b/server.py index 0ec8603..fe1ef8a 100644 --- a/server.py +++ b/server.py @@ -6,6 +6,7 @@ import logging import tornado import tornado.web from tornado.log import enable_pretty_logging +from functools import partial import mercantile import pyproj import yaml @@ -19,60 +20,8 @@ def GetTM2Source(file): tm2source = yaml.load(stream) return tm2source -def GenerateFunction(layers): - queries = [] - function = "CREATE OR REPLACE FUNCTION gettile(geometry, numeric, numeric, numeric) RETURNS SETOF bytea AS $$" - for layer in layers['Layer']: - layer_query = layer['Datasource']['table'].strip() - layer_query = layer_query[1:len(layer_query)-6] # Remove enough characters to remove first and last () and "AS t" - layer_query = layer_query.replace("geometry", "ST_AsMVTGeom(geometry,!bbox!,4096,0,true) AS mvtgeometry") - base_query = "SELECT ST_ASMVT('"+layer['id']+"', 4096, 'mvtgeometry', tile) FROM ("+layer_query+" WHERE ST_AsMVTGeom(geometry, !bbox!,4096,0,true) IS NOT NULL) AS tile" - queries.append(base_query.replace("!bbox!","$1").replace("!scale_denominator!","$2").replace("!pixel_width!","$3").replace("!pixel_height!","$4")) - function = function + " UNION ALL ".join(queries) + ";$$ LANGUAGE SQL" - print(function) - return(function) - dsn = 'postgresql://'+os.getenv('POSTGRES_USER','openmaptiles')+':'+os.getenv('POSTGRES_PASSWORD','openmaptiles')+'@'+os.getenv('POSTGRES_HOST','postgres')+':'+os.getenv('POSTGRES_PORT','5432')+'/'+os.getenv('POSTGRES_DB','openmaptiles') -def bounds(zoom,x,y): - inProj = pyproj.Proj(init='epsg:4326') - outProj = pyproj.Proj(init='epsg:3857') - lnglatbbox = mercantile.bounds(x,y,zoom) - ws = (pyproj.transform(inProj,outProj,lnglatbbox[0],lnglatbbox[1])) - en = (pyproj.transform(inProj,outProj,lnglatbbox[2],lnglatbbox[3])) - return {'w':ws[0],'s':ws[1],'e':en[0],'n':en[1]} - -def zoom_to_scale_denom(zoom): # For !scale_denominator! - # From https://github.com/openstreetmap/mapnik-stylesheets/blob/master/zoom-to-scale.txt - map_width_in_metres = 40075016.68557849 - tile_width_in_pixels = 256.0 - standardized_pixel_size = 0.00028 - map_width_in_pixels = tile_width_in_pixels*(2.0**zoom) - return str(map_width_in_metres/(map_width_in_pixels * standardized_pixel_size)) - -def replace_tokens(query,s,w,n,e,scale_denom): - return query.replace("!bbox!","ST_MakeBox2D(ST_Point("+w+", "+s+"), ST_Point("+e+", "+n+"))").replace("!scale_denominator!",scale_denom).replace("!pixel_width!","256").replace("!pixel_height!","256") - -async def get_mvt(connection,zoom,x,y): - try: # Sanitize the inputs - sani_zoom,sani_x,sani_y = float(zoom),float(x),float(y) - del zoom,x,y - except: - print('suspicious') - return 1 - - scale_denom = zoom_to_scale_denom(sani_zoom) - tilebounds = bounds(sani_zoom,sani_x,sani_y) - s,w,n,e = str(tilebounds['s']),str(tilebounds['w']),str(tilebounds['n']),str(tilebounds['e']) - final_query = "SELECT gettile(!bbox!, !scale_denominator!, !pixel_width!, !pixel_height!);" - sent_query = replace_tokens(final_query,s,w,n,e,scale_denom) - log.info(sent_query) - response = await connection.fetch(sent_query) - layers = filter(None,list(itertools.chain.from_iterable(response))) - final_tile = b'' - for layer in layers: - final_tile = final_tile + io.BytesIO(layer).getvalue() - return final_tile class GetTile(tornado.web.RequestHandler): def initialize(self, pool): @@ -83,26 +32,17 @@ class GetTile(tornado.web.RequestHandler): self.set_header("Content-Disposition", "attachment") self.set_header("Access-Control-Allow-Origin", "*") async with self.pool.acquire() as connection: - response = await get_mvt(connection, zoom,x,y) - self.write(response) - -async def get_pool(): - pool = await asyncpg.create_pool(dsn = dsn) - layers = GetTM2Source(os.getenv("MAPPING_FILE", "/mapping/data.yml")) - # Make this prepared statement from the tm2source - create_function = GenerateFunction(layers) - async with pool.acquire() as connection: - await connection.execute(create_function) - return pool + tile = await connection.fetchval("SELECT gettile($1, $2, $3)", int(zoom), int(x), int(y)) + self.write(tile) def m(): - enable_pretty_logging() - io_loop = tornado.ioloop.IOLoop.current() - pool = io_loop.run_sync(get_pool) - application = tornado.web.Application([(r"/tiles/([0-9]+)/([0-9]+)/([0-9]+).pbf", GetTile, dict(pool=pool))]) - print("Postserve started..") - application.listen(int(os.getenv("LISTEN_PORT", "8080"))) - io_loop.start() + enable_pretty_logging() + io_loop = tornado.ioloop.IOLoop.current() + pool = io_loop.run_sync(partial(asyncpg.create_pool, dsn=dsn)) + application = tornado.web.Application([(r"/tiles/([0-9]+)/([0-9]+)/([0-9]+).pbf", GetTile, dict(pool=pool))]) + print("Postserve started..") + application.listen(int(os.getenv("LISTEN_PORT", "8080"))) + io_loop.start() if __name__ == "__main__": m()