Viktigt
Översättning är en gemenskapsinsats du kan gå med i. Den här sidan är för närvarande översatt till 100.00%.
20. QGIS Server och Python
20.1. Introduktion
Om du vill veta mer om QGIS Server kan du läsa QGIS Server Guide/Manual.
QGIS Server är tre olika saker:
QGIS Server-bibliotek: ett bibliotek som tillhandahåller ett API för att skapa OGC-webbtjänster
QGIS Server FCGI: en binär FCGI-applikation
qgis_mapserv.fcgi
som tillsammans med en webbserver implementerar en uppsättning OGC-tjänster (WMS, WFS, WCS etc.) och OGC API:er (WFS3/OAPIF)QGIS Development Server: en binär applikation för utvecklingsserver
qgis_mapserver
som implementerar en uppsättning OGC-tjänster (WMS, WFS, WCS etc.) och OGC API:er (WFS3/OAPIF)
Detta kapitel i kokboken fokuserar på det första ämnet och genom att förklara användningen av QGIS Server API visar det hur det är möjligt att använda Python för att utöka, förbättra eller anpassa serverns beteende eller hur man använder QGIS Server API för att bädda in QGIS Server i ett annat program.
Det finns några olika sätt att ändra QGIS Servers beteende eller utöka dess kapacitet för att erbjuda nya anpassade tjänster eller API:er, det här är de viktigaste scenarierna du kan ställas inför:
EMBEDDING → Använd QGIS Server API från ett annat Python-program
STANDALONE → Kör QGIS Server som en fristående WSGI/HTTP-tjänst
FILTERS → Förbättra/anpassa QGIS Server med filterplugins
SERVICES → Lägg till en ny SERVICE
OGC APIs → Lägg till ett nytt OGC API
Inbäddning och fristående applikationer kräver att du använder QGIS Server Python API direkt från ett annat Python-skript eller en annan applikation. De återstående alternativen är bättre lämpade för när du vill lägga till anpassade funktioner i en binär standardapplikation för QGIS Server (FCGI eller utvecklingsserver): i det här fallet måste du skriva ett Python-plugin för serverapplikationen och registrera dina anpassade filter, tjänster eller API:er.
20.2. Grunderna i server-API:et
De grundläggande klasser som ingår i en typisk QGIS Server-applikation är:
QgsServer
serverinstansen (vanligtvis en enda instans under hela programmets livslängd)QgsServerRequest
objektet för begäran (återskapas vanligtvis vid varje begäran)QgsServer.handleRequest(request, response)
behandlar begäran och fyller i svaret
Arbetsflödet för QGIS Server FCGI eller utvecklingsserver kan sammanfattas enligt följande:
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
Inuti metoden QgsServer.handleRequest(request, response)
anropas filterpluginets callbacks och QgsServerRequest
och QgsServerResponse
görs tillgängliga för insticksprogrammen genom klassen QgsServerInterface
.
Varning
QGIS-serverklasser är inte trådsäkra, du bör alltid använda en multiprocessmodell eller containrar när du bygger skalbara applikationer baserade på QGIS Server API.
20.3. Fristående eller inbäddad
För fristående serverapplikationer eller inbäddning måste du använda de ovan nämnda serverklasserna direkt och förpacka dem i en webbserverimplementation som hanterar alla HTTP-protokollinteraktioner med klienten.
Här följer ett minimalt exempel på användning av QGIS Server API (utan HTTP-delen):
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()
Här är ett komplett fristående applikationsexempel som utvecklats för kontinuerlig integrationstestning på QGIS källkodsarkiv, det visar en bred uppsättning olika pluginfilter och autentiseringsscheman (inte avsedda för produktion eftersom de endast utvecklades för teständamål men ändå intressanta för inlärning): qgis_wrapped_server.py
20.4. Plugins för server
Python-plugins för server laddas en gång när QGIS Server-programmet startar och kan användas för att registrera filter, tjänster eller API:er.
Strukturen för ett serverplugin är mycket lik dess motsvarighet på skrivbordet, ett QgsServerInterface
-objekt görs tillgängligt för plugin-programmen och plugin-programmen kan registrera ett eller flera anpassade filter, tjänster eller API:er till motsvarande register genom att använda en av de metoder som exponeras av servergränssnittet.
20.4.1. Plugins för serverfilter
Filter finns i tre olika varianter och de kan instansieras genom att underklassa en av klasserna nedan och anropa motsvarande metod i QgsServerInterface
:
Filtertyp |
Basklass |
Registrering av QgsServerInterface |
I/O |
||
Åtkomstkontroll |
||
Cache |
20.4.1.1. I/O-filter
I/O-filter kan modifiera serverns in- och utdata (begäran och svar) för kärntjänsterna (WMS, WFS etc.), vilket gör det möjligt att manipulera tjänsternas arbetsflöde på alla sätt. Det är t.ex. möjligt att begränsa åtkomsten till utvalda lager, att injicera en XSL-stilmall i XML-svaret, att lägga till en vattenstämpel i en genererad WMS-bild och så vidare.
Från den här punkten kan det vara bra att snabbt titta på server plugins API docs.
Varje filter ska implementera minst en av tre callbacks:
Alla filter har tillgång till request/response-objektet (QgsRequestHandler
) och kan manipulera alla dess egenskaper (input/output) och skapa undantag (men på ett ganska speciellt sätt som vi ska se nedan).
Alla dessa metoder returnerar ett booleanskt värde som anger om anropet ska spridas till efterföljande filter. Om någon av dessa metoder returnerar False
så stoppas kedjan, annars kommer anropet att spridas till nästa filter.
Här följer en pseudokod som visar hur servern hanterar en typisk förfrågan och när filtrets callbacks anropas:
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
I följande stycken beskrivs de tillgängliga callbacks i detalj.
20.4.1.1.1. onRequestReady
Detta kallas när begäran är klar: inkommande URL och data har analyserats och innan de går in i kärntjänsterna (WMS, WFS etc.) växlar, detta är den punkt där du kan manipulera inmatningen och utföra åtgärder som:
autentisering/auktorisering
omdirigeringar
lägga till/ta bort vissa parametrar (t.ex. typnamn)
göra undantag
Du kan till och med ersätta en kärntjänst helt och hållet genom att ändra parametern SERVICE och därmed kringgå kärntjänsten helt och hållet (vilket dock inte är särskilt meningsfullt).
20.4.1.1.2. onSendResponse
Detta anropas närhelst någon del av utdata spolas från svarsbufferten (dvs. till FCGI stdout
om fcgi-servern används) och därifrån till klienten. Detta inträffar när stort innehåll strömmas (som WFS GetFeature). I detta fall kan onSendResponse()
anropas flera gånger.
Observera att om svaret inte är streamat, kommer onSendResponse()
inte att anropas alls.
I samtliga fall kommer den sista (eller unika) delen att skickas till klienten efter ett anrop till onResponseComplete()
.
Att returnera False
kommer att förhindra att data skickas till klienten. Detta är önskvärt när ett insticksprogram vill samla in alla delar från ett svar och undersöka eller ändra svaret i onResponseComplete()
.
20.4.1.1.3. onResponseComplete
Detta anropas en gång när kärntjänsterna (om de har träffats) har avslutat sin process och begäran är klar att skickas till klienten. Som diskuterats ovan kommer den här metoden att anropas innan den sista (eller unika) delen av data skickas till klienten. För strömningstjänster kan flera anrop till onSendResponse()
ha anropats.
onResponseComplete()
är den perfekta platsen för att implementera nya tjänster (WPS eller anpassade tjänster) och för att utföra direkt manipulation av utdata som kommer från kärntjänster (till exempel för att lägga till en vattenstämpel på en WMS-bild).
Observera att om du returnerar False
förhindras nästa plugins att utföra onResponseComplete()
, men i vilket fall som helst förhindras att svaret skickas till klienten.
20.4.1.1.4. Utlösa undantag från ett plugin
Det återstår fortfarande en del arbete med detta ämne: den nuvarande implementeringen kan skilja mellan hanterade och ohanterade undantag genom att ställa in en QgsRequestHandler
-egenskap till en instans av QgsMapServiceException, på så sätt kan den huvudsakliga C++-koden fånga hanterade pythonundantag och ignorera ohanterade undantag (eller bättre: logga dem).
Detta tillvägagångssätt fungerar i princip men det är inte särskilt ”pythoniskt”: ett bättre tillvägagångssätt skulle vara att skapa undantag från pythonkod och se dem bubbla upp till C++-loopen för att hanteras där.
20.4.1.1.5. Skriva ett serverplugin
Ett serverplugin är ett standard QGIS Python-plugin enligt beskrivningen i Utveckla Python-plugins, som bara tillhandahåller ett ytterligare (eller alternativt) gränssnitt: ett typiskt QGIS-skrivbordsplugin har tillgång till QGIS-applikationen via QgisInterface
, medan ett serverplugin endast har tillgång till QgsServerInterface
när det körs i QGIS Server-applikationskontext.
För att göra QGIS Server medveten om att ett insticksprogram har ett servergränssnitt behövs en speciell metadatapost (i metadata.txt
):
server=True
Viktigt
Endast insticksprogram som har metadatauppsättningen server=True
kommer att laddas och köras av QGIS Server.
Exempelpluginet qgis3-server-vagrant som diskuteras här (med många fler) finns tillgängligt på github, några serverplugins publiceras också i det officiella QGIS plugins repository.
20.4.1.1.5.1. Plugin-filer
Här är katalogstrukturen för vårt exempel på serverplugin.
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
Denna fil krävs av Pythons importsystem. QGIS Server kräver också att denna fil innehåller en serverClassFactory()
-funktion, som anropas när insticksprogrammet laddas in i QGIS Server när servern startar. Den tar emot en referens till en instans av QgsServerInterface
och måste returnera en instans av plugin-programmets klass. Så här ser exempelpluginet __init__.py
ut:
def serverClassFactory(serverIface):
from .HelloServer import HelloServerServer
return HelloServerServer(serverIface)
20.4.1.1.5.1.2. HelloServer.py
Det är här det magiska händer och det är så här det magiska ser ut: (t.ex. :fil:`HelloServer.py`)
Ett serverplugin består vanligtvis av en eller flera callbacks som packas in i instanser av en QgsServerFilter
.
Varje :klass:`QgsServerFilter <qgis.server.QgsServerFilter>` implementerar en eller flera av följande callbacks:
I följande exempel implementeras ett minimalt filter som skriver ut HelloServer! om parametern SERVICE är lika med ”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
Filtren måste registreras i serverIface som i följande exempel:
class HelloServerServer:
def __init__(self, serverIface):
serverIface.registerFilter(HelloFilter(serverIface), 100)
Den andra parametern i registerFilter()
anger en prioritet som definierar ordningen för callbacks med samma namn (den med lägre prioritet anropas först).
Genom att använda de tre callbacks kan plugins manipulera ingången och/eller utgången från servern på många olika sätt. I varje ögonblick har plugin-instansen tillgång till QgsRequestHandler
genom QgsServerInterface
. Klassen QgsRequestHandler
har många metoder som kan användas för att ändra ingångsparametrarna innan de går in i serverns kärnbearbetning (genom att använda requestReady()
) eller efter att begäran har bearbetats av kärntjänsterna (genom att använda sendResponse()
).
Följande exempel täcker några vanliga användningsfall:
20.4.1.1.5.2. Modifiera inmatningen
Exempelpluginet innehåller ett testexempel som ändrar inmatningsparametrar som kommer från frågesträngen, i det här exemplet injiceras en ny parameter i (redan analyserad) parameterMap
, den här parametern är sedan synlig av kärntjänster (WMS etc.), i slutet av kärntjänstbearbetningen kontrollerar vi att parametern fortfarande finns kvar:
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
Detta är ett utdrag av vad du ser i loggfilen:
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
På den markerade raden anger strängen ”SUCCESS” att insticksprogrammet klarade testet.
Samma teknik kan utnyttjas för att använda en anpassad tjänst i stället för en kärntjänst: du kan t.ex. hoppa över en WFS SERVICE-begäran eller någon annan kärntjänstbegäran bara genom att ändra SERVICE-parametern till något annat och kärntjänsten kommer att hoppas över. Sedan kan du injicera dina anpassade resultat i utdata och skicka dem till klienten (detta förklaras nedan).
Tips
Om du verkligen vill implementera en anpassad tjänst rekommenderas att du underklassar QgsService
och registrerar din tjänst på registerFilter()
genom att anropa dess registerService(service)
20.4.1.1.5.3. Modifiera eller byta ut utmatningen
Exemplet med vattenstämpelfiltret visar hur man ersätter WMS-utdata med en ny bild som erhålls genom att lägga till en vattenstämpelbild ovanpå den WMS-bild som genereras av WMS-kärntjänsten:
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
I det här exemplet kontrolleras parametervärdet SERVICE och om den inkommande begäran är en WMS GETMAP och inga undantag har ställts in av ett tidigare exekverat plugin eller av kärntjänsten (WMS i det här fallet), hämtas den WMS-genererade bilden från utmatningsbufferten och vattenstämpelbilden läggs till. Det sista steget är att rensa utmatningsbufferten och ersätta den med den nyligen genererade bilden. Observera att i en verklig situation bör vi också kontrollera den begärda bildtypen i stället för att bara stödja PNG eller JPG.
20.4.1.2. Filter för åtkomstkontroll
Filter för åtkomstkontroll ger utvecklaren en finkornig kontroll över vilka lager, funktioner och attribut som kan nås, följande callbacks kan implementeras i ett filter för åtkomstkontroll:
20.4.1.2.1. Plugin-filer
Här är katalogstrukturen för vårt exempel på plugin:
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
Den här filen krävs av Pythons importsystem. Som för alla QGIS-serverplugins innehåller denna fil en serverClassFactory()
-funktion, som anropas när pluginet laddas in i QGIS Server vid uppstart. Den tar emot en referens till en instans av QgsServerInterface
och måste returnera en instans av ditt insticksprograms klass. Så här ser exempelpluginet __init__.py
ut:
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)
Detta exempel ger full åtkomst för alla.
Det är plugin-programmets uppgift att veta vem som är inloggad.
För alla dessa metoder har vi argumentet lager på lager för att kunna anpassa begränsningen per lager.
20.4.1.2.2. layerFilterExpression
Används för att lägga till ett uttryck för att begränsa resultaten.
Till exempel för att begränsa till funktioner där attributet role
är lika med user
.
def layerFilterExpression(self, layer):
return "$role = 'user'"
20.4.1.2.3. layerFilterSubsetString
Samma som föregående men använd SubsetString
(exekveras i databasen)
Till exempel för att begränsa till funktioner där attributet role
är lika med user
.
def layerFilterSubsetString(self, layer):
return "role = 'user'"
20.4.1.2.4. layerPermissions
Begränsa åtkomsten till lagret.
Returnera ett objekt av typen LayerPermissions()
, som har egenskaperna:
canRead <qgis.server.QgsAccessControlFilter.LayerPermissions.canRead>`
för att se den iGetCapabilities
och ha läsbehörighet.canInsert
för att kunna infoga en ny funktion.canUpdate
för att kunna uppdatera en funktion.canDelete
för att kunna ta bort en funktion.
Till exempel för att begränsa allt till endast läsrättigheter:
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
Detta används för att begränsa redigeringen till en delmängd av funktionerna.
Det används i protokollet WFS-Transaction
.
Till exempel för att bara kunna redigera funktioner som har attributet role
med värdet user
:
def allowToEdit(self, layer, feature):
return feature.attribute('role') == 'user'
20.4.1.2.7. cacheKey
QGIS Server upprätthåller en cache av funktionerna och om du vill ha en cache per roll kan du returnera rollen i den här metoden. Eller returnera None
för att helt inaktivera cacheminnet.
20.4.2. Anpassade tjänster
I QGIS Server implementeras kärntjänster som WMS, WFS och WCS som underklasser till QgsService
.
Om du vill implementera en ny tjänst som körs när frågesträngparametern SERVICE
matchar tjänstens namn kan du implementera din egen QgsService
och registrera tjänsten på serviceRegistry()
genom att anropa dess registerService(service)
.
Här är ett exempel på en anpassad tjänst med namnet 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. Anpassade API:er
I QGIS Server implementeras centrala OGC API:er som OAPIF (även känt som WFS3) som samlingar av QgsServerOgcApiHandler
subklasser som är registrerade till en instans av QgsServerOgcApi
(eller dess överordnade klass QgsServerApi
).
För att implementera ett nytt API som kommer att köras när webbadressens sökväg matchar en viss URL kan du implementera dina egna QgsServerOgcApiHandler
-instanser, lägga till dem i en QgsServerOgcApi
och registrera API:et på serviceRegistry()
genom att anropa dess registerApi(api)
.
Här är ett exempel på ett anpassat API som kommer att köras när URL:en innehåller /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)