Importante
La traducción es un esfuerzo comunitario puede unirse. Esta página está actualmente traducida en |progreso de traducción|.
20. Servidor QGIS y Python
20.1. Introducción
Para obtener más información sobre QGIS Server, lea Guía/Manual de Servidor QGIS.
QGIS Server es tres cosas diferentes:
Biblioteca de QGIS Server: una biblioteca que proporciona una API para crear servicios web OGC
QGIS Server FCGI: una aplicación binaria FCGI
qgis_mapserv.fcgi
que junto con un servidor web implementa un conjunto de servicios OGC (WMS, WFS, WCS etc.) y APIs OGC (WFS3/OAPIF)QGIS Development Server: una aplicación binaria de servidor de desarrollo
qgis_mapserver
que implementa un conjunto de servicios OGC (WMS, WFS, WCS, etc.) y API de OGC (WFS3 / OAPIF)
Este capítulo del libro de cocina se centra en el primer tema y, al explicar el uso de la API de QGIS Server, muestra cómo es posible utilizar Python para ampliar, mejorar o personalizar el comportamiento del servidor o cómo utilizar la API de QGIS Server para incrustar el servidor QGIS en otra aplicación.
Hay algunas formas diferentes en las que puede alterar el comportamiento de QGIS Server o ampliar sus capacidades para ofrecer nuevos servicios personalizados o API, estos son los principales escenarios a los que puede enfrentarse:
EMBEDDING → Utilice la API de QGIS Server desde otra aplicación de Python
STANDALONE → Ejecutar QGIS Server como servicio autónomo WSGI/HTTP
FILTERS → Mejorar/personalizar QGIS Server con complementos de filtro
SERVICES → Añadir un nuevo SERVICE
OGC APIs → Añadir una nueva OGC API
Las aplicaciones integradas e independientes requieren el uso de la API de Python de QGIS Server directamente desde otro script o aplicación de Python. Las opciones restantes son más adecuadas para cuando desee agregar funciones personalizadas a una aplicación binaria estándar de QGIS Server (FCGI o servidor de desarrollo): en este caso, deberá escribir un complemento de Python para la aplicación del servidor y registrar sus filtros personalizados, servicios o API.
20.2. Conceptos básicos de la API del servidor
Las clases fundamentales involucradas en una aplicación típica de QGIS Server son:
QgsServer
la instancia del servidor (normalmente una sola instancia durante toda la vida de la aplicación)QgsServerRequest
el objeto de la solicitud (normalmente recreado en cada solicitud)QgsServer.handleRequest(request, response)
procesa la solicitud y completa la respuesta
El flujo de trabajo del servidor de desarrollo o FCGI de QGIS Server se puede resumir de la siguiente manera:
1initialize the QgsApplication
2create the QgsServer
3the main server loop waits forever for client requests:
4 for each incoming request:
5 create a QgsServerRequest request
6 create a QgsServerResponse response
7 call QgsServer.handleRequest(request, response)
8 filter plugins may be executed
9 send the output to the client
Dentro del métodod QgsServer.handleRequest(request, response)
las devoluciones de llamada de los complementos de filtro se llaman y QgsServerRequest
y QgsServerResponse
están disponibles para los complementos a través de la clase QgsServerInterface
Advertencia
Las clases de servidor de QGIS no son seguras para subprocesos, siempre debe usar un modelo o contenedores de multiprocesamiento cuando cree aplicaciones escalables basadas en la API de QGIS Server.
20.3. Independiente o incrustado
Para aplicaciones de servidor independientes o incrustaciones, deberá usar las clases de servidor mencionadas anteriormente directamente, envolviéndolas en una implementación de servidor web que gestiona todas las interacciones del protocolo HTTP con el cliente.
A continuación, se muestra un ejemplo mínimo del uso de la API de QGIS Server (sin la parte HTTP):
1from qgis.core import QgsApplication
2from qgis.server import *
3app = QgsApplication([], False)
4
5# Create the server instance, it may be a single one that
6# is reused on multiple requests
7server = QgsServer()
8
9# Create the request by specifying the full URL and an optional body
10# (for example for POST requests)
11request = QgsBufferServerRequest(
12 'http://localhost:8081/?MAP=/qgis-server/projects/helloworld.qgs' +
13 '&SERVICE=WMS&REQUEST=GetCapabilities')
14
15# Create a response objects
16response = QgsBufferServerResponse()
17
18# Handle the request
19server.handleRequest(request, response)
20
21print(response.headers())
22print(response.body().data().decode('utf8'))
23
24app.exitQgis()
A continuación se muestra un ejemplo completo de aplicación independiente desarrollada para las pruebas de integraciones continuas en el repositorio de código fuente de QGIS, que muestra un amplio conjunto de diferentes filtros de complementos y esquemas de autenticación (no significa para la producción, ya que se desarrollaron sólo para fines de prueba, pero sigue siendo interesante para el aprendizaje): qgis_wrapped_server.py
20.4. Complementos del servidor
Los complementos de Python del servidor se cargan una vez cuando se inicia la aplicación QGIS Server y se pueden usar para registrar filtros, servicios o API.
La estructura de un complemento de servidor es muy similar a su contraparte de escritorio, un objeto QgsServerInterface
está disponible para los complementos y los complementos pueden registrar uno o más filtros personalizados, servicios o API en el registro correspondiente mediante uno de los métodos expuestos por la interfaz del servidor.
20.4.1. Complementos de filtro de servidor
Los filtros vienen en tres sabores diferentes y se pueden instanciar subclasificando una de las clases a continuación y llamando al método correspondiente de QgsServerInterface
:
Tipo de filtro |
Clase Base |
Registro de QgsServerInterface |
E/S |
||
Control de acceso |
||
Cache |
20.4.1.1. Filtros de E/S
Los filtros de E/S pueden modificar la entrada y salida del servidor (la solicitud y la respuesta) de los servicios centrales (WMS, WFS, etc.) permitiendo realizar cualquier tipo de manipulación del flujo de trabajo de los servicios. Es posible, por ejemplo, restringir el acceso a las capas seleccionadas, inyectar una hoja de estilo XSL a la respuesta XML, agregar una marca de agua a una imagen WMS generada, etc.
A partir de este punto, encontrará útil un vistazo rápido a server plugins API docs.
Cada filtro debe implementar al menos una de las tres devoluciones de llamada:
Todos los filtros tienen acceso al objeto de solicitud/respuesta (QgsRequestHandler
) y puede manipular todas sus propiedades (entrada/salida) y generar excepciones (aunque de una manera bastante particular como veremos a continuación).
Todos estos métodos devuelven un valor booleano que indica si la llamada debe propagarse a los filtros siguientes. Si uno de estos métodos devuelve False
entonces la cadena se detiene, de lo contrario la llamada se propagará al siguiente filtro.
Aquí está el pseudocódigo que muestra cómo el servidor maneja una solicitud típica y cuándo se llaman las devoluciones de llamada del filtro:
1for each incoming request:
2 create GET/POST request handler
3 pass request to an instance of QgsServerInterface
4 call onRequestReady filters
5
6 if there is not a response:
7 if SERVICE is WMS/WFS/WCS:
8 create WMS/WFS/WCS service
9 call service’s executeRequest
10 possibly call onSendResponse for each chunk of bytes
11 sent to the client by a streaming services (WFS)
12 call onResponseComplete
13 request handler sends the response to the client
Los siguientes párrafos describen las devoluciones de llamada disponibles en detalle.
20.4.1.1.1. onRequestReady
Esto se llama cuando la solicitud está lista: la URL entrante y los datos se han analizado y antes de ingresar al conmutador de servicios centrales (WMS, WFS, etc.), este es el punto donde puede manipular la entrada y realizar acciones como:
autenticación/autorización
redirije
añadir/borrar ciertos parámetros (nombres de tipos, por ejemplo)
plantear excepciones
Incluso podría sustituir un servicio central por completo cambiando el parámetro SERVICE y, por lo tanto, omitiendo el servicio central por completo (aunque esto no tiene mucho sentido).
20.4.1.1.2. onSendResponse
Esta llamada se realiza siempre que cualquier salida parcial se descarga desde el buffer de respuesta (es decir, a FCGI stdout
si se utiliza el servidor fcgi) y desde allí, al cliente. Esto ocurre cuando se transmite un contenido enorme (como WFS GetFeature). En este caso onSendResponse()
puede ser llamado varias veces.
Tenga en cuenta que si la respuesta no se transmite, entonces onSendResponse()
no se llamará en absoluto.
En todos los casos, el último (o único) trozo se enviará al cliente tras una llamada a onResponseComplete()
.
Devolver False
prevendrá la descarga de datos al cliente. Esto es deseable cuando un complemento quiere recoger todos los trozos de una respuesta y examinar o cambiar la respuesta en onResponseComplete()
.
20.4.1.1.3. onResponseComplete
Se llama una vez cuando los servicios centrales (si se han alcanzado) terminan su proceso y la petición está lista para ser enviada al cliente. Como se ha comentado anteriormente, este método se llamará antes de que el último (o único) trozo de datos se envíe al cliente. Para servicios de streaming, multiples llamadas a onSendResponse()
pueden haber sido llamadas.
onResponseComplete()
es el lugar ideal para proporcionar la implementación de nuevos servicios (WPS o servicios personalizados) y para realizar la manipulación directa de la salida procedente de los servicios centrales (por ejemplo para añadir una marca de agua sobre una imagen WMS).
Tenga en cuenta que devolver False
evitará que los siguientes complementos ejecuten onResponseComplete()
pero, en cualquier caso, evitará que se envíe la respuesta al cliente.
20.4.1.1.4. Generación de excepciones de un complemento
Aún queda trabajo por hacer en este tema: la implementación actual puede distinguir entre excepciones manejadas y no manejadas estableciendo una propiedad QgsRequestHandler
a una instancia de QgsMapServiceException, de esta manera el código principal de C ++ puede detectar excepciones de Python controladas e ignorar las excepciones no controladas (o mejor: registrarlas).
Este enfoque básicamente funciona, pero no es muy «pitónico»: un mejor enfoque sería generar excepciones del código de Python y verlas burbujear en el bucle C ++ para que se manejen allí.
20.4.1.1.5. Escribiendo un complemento del servidor
Un complemento de servidor es un complemento estándar de QGIS Python como se describe en Desarrollando Plugins Python, que solo proporciona una interfaz adicional (o alternativa): un complemento de escritorio QGIS típico tiene acceso a la aplicación QGIS a través de: class:` QgisInterface <qgis. gui.QgisInterface> , un complemento de servidor solo tiene acceso a :class:`QgsServerInterface <qgis.server.QgsServerInterface> cuando se ejecuta dentro del contexto de la aplicación QGIS Server.
Para que QGIS Server sepa que un complemento tiene una interfaz de servidor, se necesita una entrada de metadatos especial (en metadata.txt
):
server=True
Importante
QGIS Server solo cargará y ejecutará los complementos que tengan el conjunto de metadatos server=True
.
El complemento de ejemplo qgis3-server-vagrant discutido aquí (con muchos más) está disponible en github, algunos complementos de servidor también están publicados en el repositorio oficial de complementos QGIS.
20.4.1.1.5.1. Archivos de complementos
Aquí está la estructura de directorios de nuestro complemento de servidor de ejemplo.
1PYTHON_PLUGINS_PATH/
2 HelloServer/
3 __init__.py --> *required*
4 HelloServer.py --> *required*
5 metadata.txt --> *required*
20.4.1.1.5.1.1. __init__.py
Este archivo es requerido por el sistema de importación de Python. Además, QGIS Server requiere que este archivo contenga una función serverClassFactory()
, que se llama cuando el complemento se carga en QGIS Server cuando se inicia el servidor. Recibe una referencia a la instancia de QgsServerInterface
y debe devolver la instancia de la clase de su complemento. Así es como se ve el complemento de ejemplo __init __. Py
:
def serverClassFactory(serverIface):
from .HelloServer import HelloServerServer
return HelloServerServer(serverIface)
20.4.1.1.5.1.2. HelloServer.py
Aquí es donde ocurre la magia y así es como se ve la magia: (por ejemplo HelloServer.py
)
Un complemento de servidor generalmente consiste en una o más devoluciones de llamada empaquetadas en instancias de QgsServerFilter
.
Cada QgsServerFilter
implementa una o más de las siguientes devoluciones de llamada:
El siguiente ejemplo implementa un filtro mínimo que imprime HelloServer! En caso de que el parámetro SERVICE sea igual a «HELLO»:
1class HelloFilter(QgsServerFilter):
2
3 def __init__(self, serverIface):
4 super().__init__(serverIface)
5
6 def onRequestReady(self) -> bool:
7 QgsMessageLog.logMessage("HelloFilter.onRequestReady")
8 return True
9
10 def onSendResponse(self) -> bool:
11 QgsMessageLog.logMessage("HelloFilter.onSendResponse")
12 return True
13
14 def onResponseComplete(self) -> bool:
15 QgsMessageLog.logMessage("HelloFilter.onResponseComplete")
16 request = self.serverInterface().requestHandler()
17 params = request.parameterMap()
18 if params.get('SERVICE', '').upper() == 'HELLO':
19 request.clear()
20 request.setResponseHeader('Content-type', 'text/plain')
21 # Note that the content is of type "bytes"
22 request.appendBody(b'HelloServer!')
23 return True
Los filtros deben registrarse en serverIface como en el siguiente ejemplo:
class HelloServerServer:
def __init__(self, serverIface):
serverIface.registerFilter(HelloFilter(serverIface), 100)
El segundo parámetro del método registerFilter()
establece una prioridad que define el orden de las devoluciones de llamada con el mismo nombre (la prioridad más baja se invoca primero).
Al utilizar las tres devoluciones de llamada, los complementos pueden manipular la entrada y / o la salida del servidor de muchas formas diferentes. En todo momento, la instancia del complemento tiene acceso a QgsRequestHandler
a través de QgsServerInterface
. La clase QgsRequestHandler
tiene muchos métodos que se pueden usar para modificar los parámetros de entrada antes de ingresar al procesamiento central del servidor (usando requestReady()
) o después de la solicitud ha sido procesado por los servicios centrales (usando sendResponse()
).
Los siguientes ejemplos cubren algunos casos comunes de uso:
20.4.1.1.5.2. Modificando la entrada
El complemento de ejemplo contiene un ejemplo de prueba que cambia los parámetros de entrada provenientes de la cadena de consulta, en este ejemplo se inyecta un nuevo parámetro en el (ya analizado) parameterMap
, este parámetro es visible luego por los servicios centrales (WMS, etc.) , al final del procesamiento de los servicios centrales, verificamos que el parámetro todavía esté allí:
1class ParamsFilter(QgsServerFilter):
2
3 def __init__(self, serverIface):
4 super(ParamsFilter, self).__init__(serverIface)
5
6 def onRequestReady(self) -> bool:
7 request = self.serverInterface().requestHandler()
8 params = request.parameterMap( )
9 request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')
10 return True
11
12 def onResponseComplete(self) -> bool:
13 request = self.serverInterface().requestHandler()
14 params = request.parameterMap( )
15 if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
16 QgsMessageLog.logMessage("SUCCESS - ParamsFilter.onResponseComplete")
17 else:
18 QgsMessageLog.logMessage("FAIL - ParamsFilter.onResponseComplete")
19 return True
Esto es un extracto de lo que puede ver en el archivo de log:
1 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloServerServer - loading filter ParamsFilter
2 src/core/qgsmessagelog.cpp: 45: (logMessage) [1ms] 2014-12-12T12:39:29 Server[0] Server plugin HelloServer loaded!
3 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 Server[0] Server python plugins loaded
4 src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [1ms] inserting pair SERVICE // HELLO into the parameter map
5 src/mapserver/qgsserverfilter.cpp: 42: (onRequestReady) [0ms] QgsServerFilter plugin default onRequestReady called
6 src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] SUCCESS - ParamsFilter.onResponseComplete
En la línea resaltada, la cadena «SUCCESS» indica que el complemento pasó la prueba.
La misma técnica se puede aprovechar para usar un servicio personalizado en lugar de uno central: por ejemplo, podría omitir una solicitud SERVICE WFS o cualquier otra solicitud principal simplemente cambiando el parámetro SERVICE a algo diferente y se omitirá el servicio principal. Luego, puede inyectar sus resultados personalizados en la salida y enviarlos al cliente (esto se explica a continuación).
Truco
Si realmente desea implementar un servicio personalizado, se recomienda crear una subclase QgsService
y registre su servicio en registerFilter()
llamando a su método registerService(service)
20.4.1.1.5.3. Modificar o reemplazar la salida
El ejemplo del filtro de marca de agua muestra cómo reemplazar la salida de WMS con una nueva imagen obtenida agregando una imagen de marca de agua en la parte superior de la imagen de WMS generada por el servicio principal de WMS:
1from qgis.server import *
2from qgis.PyQt.QtCore import *
3from qgis.PyQt.QtGui import *
4
5class WatermarkFilter(QgsServerFilter):
6
7 def __init__(self, serverIface):
8 super().__init__(serverIface)
9
10 def onResponseComplete(self) -> bool:
11 request = self.serverInterface().requestHandler()
12 params = request.parameterMap( )
13 # Do some checks
14 if (params.get('SERVICE').upper() == 'WMS' \
15 and params.get('REQUEST').upper() == 'GETMAP' \
16 and not request.exceptionRaised() ):
17 QgsMessageLog.logMessage("WatermarkFilter.onResponseComplete: image ready %s" % request.parameter("FORMAT"))
18 # Get the image
19 img = QImage()
20 img.loadFromData(request.body())
21 # Adds the watermark
22 watermark = QImage(os.path.join(os.path.dirname(__file__), 'media/watermark.png'))
23 p = QPainter(img)
24 p.drawImage(QRect( 20, 20, 40, 40), watermark)
25 p.end()
26 ba = QByteArray()
27 buffer = QBuffer(ba)
28 buffer.open(QIODevice.WriteOnly)
29 img.save(buffer, "PNG" if "png" in request.parameter("FORMAT") else "JPG")
30 # Set the body
31 request.clearBody()
32 request.appendBody(ba)
33 return True
En este ejemplo, el valor del parámetro SERVICE se verifica y si la solicitud entrante es un WMS GETMAP y no se han establecido excepciones por un complemento ejecutado previamente o por el servicio central (WMS en este caso), la imagen generada por WMS se recupera del búfer de salida y se agrega la imagen de marca de agua. El último paso es borrar el búfer de salida y reemplazarlo con la imagen recién generada. Tenga en cuenta que, en una situación del mundo real, también deberíamos comprobar el tipo de imagen solicitada en lugar de admitir solo PNG o JPG.
20.4.1.2. Filtros de control de acceso
Los filtros de control de acceso brindan al desarrollador un control detallado sobre las capas, características y atributos a los que se puede acceder; las siguientes devoluciones de llamada se pueden implementar en un filtro de control de acceso:
20.4.1.2.1. Archivos de complementos
Aquí está la estructura de directorios de nuestro complemento de ejemplo:
1PYTHON_PLUGINS_PATH/
2 MyAccessControl/
3 __init__.py --> *required*
4 AccessControl.py --> *required*
5 metadata.txt --> *required*
20.4.1.2.1.1. __init__.py
Este archivo es requerido por el sistema de importación de Python. Como para todos los complementos del servidor QGIS, este archivo contiene una función serverClassFactory ()
, que se llama cuando el complemento se carga en el servidor QGIS al inicio. Recibe una referencia a una instancia de QgsServerInterface
y debe devolver una instancia de la clase de su complemento. Así es como el complemento de ejemplo __init__.py
aparece:
def serverClassFactory(serverIface):
from MyAccessControl.AccessControl import AccessControlServer
return AccessControlServer(serverIface)
20.4.1.2.1.2. AccessControl.py
1class AccessControlFilter(QgsAccessControlFilter):
2
3 def __init__(self, server_iface):
4 super().__init__(server_iface)
5
6 def layerFilterExpression(self, layer):
7 """ Return an additional expression filter """
8 return super().layerFilterExpression(layer)
9
10 def layerFilterSubsetString(self, layer):
11 """ Return an additional subset string (typically SQL) filter """
12 return super().layerFilterSubsetString(layer)
13
14 def layerPermissions(self, layer):
15 """ Return the layer rights """
16 return super().layerPermissions(layer)
17
18 def authorizedLayerAttributes(self, layer, attributes):
19 """ Return the authorised layer attributes """
20 return super().authorizedLayerAttributes(layer, attributes)
21
22 def allowToEdit(self, layer, feature):
23 """ Are we authorised to modify the following geometry """
24 return super().allowToEdit(layer, feature)
25
26 def cacheKey(self):
27 return super().cacheKey()
28
29class AccessControlServer:
30
31 def __init__(self, serverIface):
32 """ Register AccessControlFilter """
33 serverIface.registerAccessControl(AccessControlFilter(serverIface), 100)
Este ejemplo otorga acceso total para todos.
Es rol del complemento saber quién ha ingresado.
En todos esos métodos tenemos el argumento layer on para poder personalizar la restricción por capa.
20.4.1.2.2. layerFilterExpression
Permite añadir una expresión para limitar los resultados.
Por ejemplo, para limitar a características donde el atributo role
es igual a user
.
def layerFilterExpression(self, layer):
return "$role = 'user'"
20.4.1.2.3. layerFilterSubsetString
Igual que el anterior pero usa el SubsetString
(ejecutado en la base de datos)
Por ejemplo, para limitar a características donde el atributo role
es igual a user
.
def layerFilterSubsetString(self, layer):
return "role = 'user'"
20.4.1.2.4. layerPermissions
Limitar el acceso a la capa.
Devuelve un objeto de tipo LayerPermissions()
, el cuál tiene las propiedades:
canRead
para verlo enGetCapabilities
y tiene acceso de lectura.canInsert
para poder insertar una nueva característica.canUpdate
para ser capaz de actualizar una característica.canDelete
para ser capaz de borrar una característica.
Por ejemplo, para limitar todo al acceso de sólo lectura:
1def layerPermissions(self, layer):
2 rights = QgsAccessControlFilter.LayerPermissions()
3 rights.canRead = True
4 rights.canInsert = rights.canUpdate = rights.canDelete = False
5 return rights
20.4.1.2.6. allowToEdit
Esto es usado para limitar la edición de un subconjunto de objetos espaciales.
Se utiliza en el protocolo WFS-Transaction
.
Por ejemplo, para poder editar sólo el objeto espacial que tiene el atributo role
con el valor user
:
def allowToEdit(self, layer, feature):
return feature.attribute('role') == 'user'
20.4.1.2.7. cacheKey
QGIS Server mantiene una caché de las capacidades entonces para tener una caché por rol puedes retornar el rol en este método. O devolver None
para desactivar completamente la caché.
20.4.2. Servicios personalizados
En QGIS Server, los servicios centrales como WMS, WFS y WCS se implementan como subclases de QgsService
.
Para implementar un nuevo servicio que se ejecutará cuando el parámetro de cadena de consulta SERVICE
coincida con el nombre del servicio, puedes implementar tu propio QgsService
y registrar tu servicio en el serviceRegistry()
llamando a su registerService(service)
.
Este es un ejemplo de un servicio personalizado llamado CUSTOM
:
1from qgis.server import QgsService
2from qgis.core import QgsMessageLog
3
4class CustomServiceService(QgsService):
5
6 def __init__(self):
7 QgsService.__init__(self)
8
9 def name(self):
10 return "CUSTOM"
11
12 def version(self):
13 return "1.0.0"
14
15 def executeRequest(self, request, response, project):
16 response.setStatusCode(200)
17 QgsMessageLog.logMessage('Custom service executeRequest')
18 response.write("Custom service executeRequest")
19
20
21class CustomService():
22
23 def __init__(self, serverIface):
24 serverIface.serviceRegistry().registerService(CustomServiceService())
20.4.3. APIs personalizadas
En QGIS Server, las API principales de OGC como OAPIF (también conocido como WFS3) se implementan como colecciones de QgsServerOgcApiHandler
subclases que están registradas en una instancia de QgsServerOgcApi
).
Para implementar una nueva API que se ejecute cuando la ruta url coincida con una determinada URL, puede implementar sus propias instancias QgsServerOgcApiHandler
, añadirlas a una QgsServerOgcApi
y registrar la API en el serviceRegistry()
llamando a su registerApi(api)
.
A continuación, se muestra un ejemplo de una API personalizada que se ejecutará cuando la URL contenga /customapi
:
1import json
2import os
3
4from qgis.PyQt.QtCore import QBuffer, QIODevice, QTextStream, QRegularExpression
5from qgis.server import (
6 QgsServiceRegistry,
7 QgsService,
8 QgsServerFilter,
9 QgsServerOgcApi,
10 QgsServerQueryStringParameter,
11 QgsServerOgcApiHandler,
12)
13
14from qgis.core import (
15 QgsMessageLog,
16 QgsJsonExporter,
17 QgsCircle,
18 QgsFeature,
19 QgsPoint,
20 QgsGeometry,
21)
22
23
24class CustomApiHandler(QgsServerOgcApiHandler):
25
26 def __init__(self):
27 super(CustomApiHandler, self).__init__()
28 self.setContentTypes([QgsServerOgcApi.HTML, QgsServerOgcApi.JSON])
29
30 def path(self):
31 return QRegularExpression("/customapi")
32
33 def operationId(self):
34 return "CustomApiXYCircle"
35
36 def summary(self):
37 return "Creates a circle around a point"
38
39 def description(self):
40 return "Creates a circle around a point"
41
42 def linkTitle(self):
43 return "Custom Api XY Circle"
44
45 def linkType(self):
46 return QgsServerOgcApi.data
47
48 def handleRequest(self, context):
49 """Simple Circle"""
50
51 values = self.values(context)
52 x = values['x']
53 y = values['y']
54 r = values['r']
55 f = QgsFeature()
56 f.setAttributes([x, y, r])
57 f.setGeometry(QgsCircle(QgsPoint(x, y), r).toCircularString())
58 exporter = QgsJsonExporter()
59 self.write(json.loads(exporter.exportFeature(f)), context)
60
61 def templatePath(self, context):
62 # The template path is used to serve HTML content
63 return os.path.join(os.path.dirname(__file__), 'circle.html')
64
65 def parameters(self, context):
66 return [QgsServerQueryStringParameter('x', True, QgsServerQueryStringParameter.Type.Double, 'X coordinate'),
67 QgsServerQueryStringParameter(
68 'y', True, QgsServerQueryStringParameter.Type.Double, 'Y coordinate'),
69 QgsServerQueryStringParameter('r', True, QgsServerQueryStringParameter.Type.Double, 'radius')]
70
71
72class CustomApi():
73
74 def __init__(self, serverIface):
75 api = QgsServerOgcApi(serverIface, '/customapi',
76 'custom api', 'a custom api', '1.1')
77 handler = CustomApiHandler()
78 api.registerHandler(handler)
79 serverIface.serviceRegistry().registerApi(api)