Merge 4fed59c72393e7c0d0f8f02deb4aa78e866b108a into 66d17625d85aa144e82702dbd1cf31aadf3d094d

This commit is contained in:
Eldar Yusupov 2019-10-21 02:33:32 +00:00 committed by GitHub
commit f1255c8f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 86 deletions

View File

@ -1,4 +1,4 @@
FROM python:3.4 FROM python:3.7
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app WORKDIR /usr/src/app

View File

@ -1,6 +1,6 @@
tornado==4.4.2 asyncpg==0.19.0
sqlalchemy==1.1.5 Click==7.0
mercantile==0.9.0 mercantile==1.1.2
pyproj==1.9.5.1 pyproj==2.4.0
pyyaml==3.12 PyYAML==5.1.2
psycopg2==2.6.2 tornado==6.0.3

104
server.py
View File

@ -1,102 +1,48 @@
import tornado.ioloop import asyncpg
import tornado.web
import io import io
import os import os
from sqlalchemy import Column, ForeignKey, Integer, String import logging
from sqlalchemy.ext.declarative import declarative_base import tornado
from sqlalchemy.orm import relationship import tornado.web
from sqlalchemy import create_engine from tornado.log import enable_pretty_logging
from sqlalchemy import inspect from functools import partial
from sqlalchemy import text
from sqlalchemy.orm import sessionmaker
import mercantile import mercantile
import pyproj import pyproj
import yaml import yaml
import sys import sys
import itertools import itertools
log = logging.getLogger('tornado.application')
def GetTM2Source(file): def GetTM2Source(file):
with open(file,'r') as stream: with open(file,'r') as stream:
tm2source = yaml.load(stream) tm2source = yaml.load(stream)
return tm2source return tm2source
def GeneratePrepared(layers): 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')
queries = []
prepared = "PREPARE gettile(geometry, numeric, numeric, numeric) AS "
for layer in layers['Layer']:
layer_query = layer['Datasource']['table'].lstrip().rstrip() # Remove lead and trailing whitespace
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"))
prepared = prepared + " UNION ALL ".join(queries) + ";"
print(prepared)
return(prepared)
layers = GetTM2Source("/mapping/data.yml")
prepared = GeneratePrepared(layers)
engine = create_engine('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'))
inspector = inspect(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
session.execute(prepared)
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")
def get_mvt(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 = "EXECUTE gettile(!bbox!, !scale_denominator!, !pixel_width!, !pixel_height!);"
sent_query = replace_tokens(final_query,s,w,n,e,scale_denom)
response = list(session.execute(sent_query))
print(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): class GetTile(tornado.web.RequestHandler):
def get(self, zoom,x,y): def initialize(self, pool):
self.pool = pool
async def get(self, zoom,x,y):
self.set_header("Content-Type", "application/x-protobuf") self.set_header("Content-Type", "application/x-protobuf")
self.set_header("Content-Disposition", "attachment") self.set_header("Content-Disposition", "attachment")
self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Origin", "*")
response = get_mvt(zoom,x,y) async with self.pool.acquire() as connection:
self.write(response) tile = await connection.fetchval("SELECT gettile($1, $2, $3)", int(zoom), int(x), int(y))
self.write(tile)
def m(): def m():
if __name__ == "__main__": enable_pretty_logging()
# Make this prepared statement from the tm2source io_loop = tornado.ioloop.IOLoop.current()
application = tornado.web.Application([(r"/tiles/([0-9]+)/([0-9]+)/([0-9]+).pbf", GetTile)]) pool = io_loop.run_sync(partial(asyncpg.create_pool, dsn=dsn))
print("Postserve started..") application = tornado.web.Application([(r"/tiles/([0-9]+)/([0-9]+)/([0-9]+).pbf", GetTile, dict(pool=pool))])
application.listen(8080) print("Postserve started..")
tornado.ioloop.IOLoop.instance().start() application.listen(int(os.getenv("LISTEN_PORT", "8080")))
io_loop.start()
m() if __name__ == "__main__":
m()