Skip to content
Browse files
[tests] Add XYX slippy map to test QGIS server
Plus: multi-threading server side (for speed)

This is useful for testing various authentication
shemes on a slippy-map server (that uses multi-threading
in QGIS desktop)
  • Loading branch information
elpaso committed Nov 9, 2017
1 parent 3fc4be3 commit f29c9c99656b219209efec66b46818c06ff7433c
Showing 1 changed file with 52 additions and 2 deletions.
@@ -6,6 +6,11 @@
specified on the environment variable QGIS_SERVER_PORT.
QGIS_SERVER_HOST (defaults to
A XYZ slippy maps service is also available for multithreading testing:
For testing purposes, HTTP Basic can be enabled by setting the following
environment variables:
@@ -49,9 +54,13 @@
import sys
import signal
import ssl
import math
import urllib.parse
from http.server import BaseHTTPRequestHandler, HTTPServer
from qgis.core import QgsApplication
from socketserver import ThreadingMixIn
import threading

from qgis.core import QgsApplication, QgsCoordinateTransform, QgsCoordinateReferenceSystem
from qgis.server import QgsServer, QgsServerRequest, QgsBufferServerRequest, QgsBufferServerResponse

QGIS_SERVER_PORT = int(os.environ.get('QGIS_SERVER_PORT', '8081'))
@@ -100,6 +109,42 @@ def responseComplete(self):

def num2deg(xtile, ytile, zoom):
"""This returns the NW-corner of the square. Use the function with xtile+1 and/or ytile+1
to get the other corners. With xtile+0.5 & ytile+0.5 it will return the center of the tile."""
n = 2.0 ** zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
lat_deg = math.degrees(lat_rad)
return (lat_deg, lon_deg)

class XYZFilter(QgsServerFilter):
"""XYZ server, example: ?MAP=/path/to/projects.qgs&SERVICE=XYZ&X=1&Y=0&Z=1&LAYERS=world"""

def requestReady(self):
handler = self.serverInterface().requestHandler()
if handler.parameter('SERVICE') == 'XYZ':
x = int(handler.parameter('X'))
y = int(handler.parameter('Y'))
z = int(handler.parameter('Z'))
# NW corner
lat_deg, lon_deg = num2deg(x, y, z)
# SE corner
lat_deg2, lon_deg2 = num2deg(x + 1, y + 1, z)
handler.setParameter('SERVICE', 'WMS')
handler.setParameter('REQUEST', 'GetMap')
handler.setParameter('VERSION', '1.3.0')
handler.setParameter('SRS', 'EPSG:4326')
handler.setParameter('HEIGHT', '256')
handler.setParameter('WIDTH', '256')
handler.setParameter('BBOX', "{},{},{},{}".format(lat_deg2, lon_deg, lat_deg, lon_deg2))

xyzfilter = XYZFilter(qgs_server.serverInterface())

class Handler(BaseHTTPRequestHandler):

def do_GET(self, post_body=None):
@@ -128,8 +173,13 @@ def do_POST(self):
return self.do_GET(post_body)

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""

if __name__ == '__main__':
server = ThreadedHTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler)
if https:
server.socket = ssl.wrap_socket(server.socket,

0 comments on commit f29c9c9

Please sign in to comment.