125 changes: 125 additions & 0 deletions python/server/qgswfserver.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/***************************************************************************
qgswfsserver.sip
-------------------
begin : May 2, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSWFSSERVER_H
#define QGSWFSSERVER_H

#include <QDomDocument>
#include <QMap>
#include <QString>
#include <map>
#include "qgis.h"
#include "qgsowsserver.h"
#include "qgsvectorlayer.h"
#include "qgswfsprojectparser.h"

class QgsCoordinateReferenceSystem;
class QgsComposerLayerItem;
class QgsComposerLegendItem;
class QgsComposition;
class QgsFields;
class QgsMapLayer;
class QgsMapRenderer;
class QgsPoint;
class QgsRasterLayer;
class QgsConfigParser;
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
class QgsField;
class QgsFeature;
class QgsRectangle;
class QgsGeometry;
class QgsSymbol;
class QgsRequestHandler;
class QFile;
class QFont;
class QImage;
class QPaintDevice;
class QPainter;


/**This class handles all the wms server requests. The parameters and values have to be passed in the form of
a map<QString, QString>. This map is usually generated by a subclass of QgsWMSRequestHandler, which makes QgsWFSServer
independent from any server side technology*/

class QgsWFSServer: public QgsOWSServer
{
public:
/**Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWFSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWFSProjectParser* cp,
QgsRequestHandler* rh );
~QgsWFSServer();

void executeRequest() override;

/**Returns an XML file with the capabilities description (as described in the WFS specs)*/
QDomDocument getCapabilities();

/**Returns an XML file with the describe feature type (as described in the WFS specs)*/
QDomDocument describeFeatureType();

/**Creates a document that describes the result of the getFeature request.
@return 0 in case of success*/
int getFeature( QgsRequestHandler& request, const QString& format );

/**Read and apply the transaction
@return 0 in case of success*/
QDomDocument transaction( const QString& requestBody );

/**Sets configuration parser for administration settings. Does not take ownership*/
void setAdminConfigParser( QgsWFSProjectParser* parser ) { mConfigParser = parser; }

private:
/**Don't use the default constructor*/
QgsWFSServer();

/**Get service address from REQUEST_URI if not specified in the configuration*/
QString serviceUrl() const;

/* The Type of Feature created */
QString mTypeName;
/* The list of Feature's Type requested */
QStringList mTypeNames;
QString mPropertyName;
bool mWithGeom;
/* Error messages */
QStringList mErrors;

QgsWFSProjectParser* mConfigParser;

protected:

void startGetFeature( QgsRequestHandler& request, const QString& format, int prec, QgsCoordinateReferenceSystem& crs, QgsRectangle* rect );
void setGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, int prec, QgsCoordinateReferenceSystem& crs, QgsAttributeList attrIndexes, QSet<QString> excludedAttributes );
void endGetFeature( QgsRequestHandler& request, const QString& format );

//method for transaction
QgsFeatureIds getFeatureIdsFromFilter( QDomElement filter, QgsVectorLayer* layer );

//methods to write GeoJSON
QString createFeatureGeoJSON( QgsFeature* feat, int prec, QgsCoordinateReferenceSystem& crs, QgsAttributeList attrIndexes, QSet<QString> excludedAttributes ) /*const*/;

//methods to write GML2
QDomElement createFeatureGML2( QgsFeature* feat, QDomDocument& doc, int prec, QgsCoordinateReferenceSystem& crs, QgsAttributeList attrIndexes, QSet<QString> excludedAttributes ) /*const*/;

//methods to write GML3
QDomElement createFeatureGML3( QgsFeature* feat, QDomDocument& doc, int prec, QgsCoordinateReferenceSystem& crs, QgsAttributeList attrIndexes, QSet<QString> excludedAttributes ) /*const*/;

void addTransactionResult( QDomDocument& responseDoc, QDomElement& responseElem, const QString& status, const QString& locator, const QString& message );
};

#endif
47 changes: 47 additions & 0 deletions python/server/qgswfsprojectparser.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/***************************************************************************
qgswfsprojectparser.sip
------------------------
begin : May 2, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/


class QgsWFSProjectParser
{
%TypeHeaderCode
#include "qgswfsprojectparser.h"

%End

public:
QgsWFSProjectParser( const QString& filePath );
~QgsWFSProjectParser();

void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
QString serviceUrl() const;
QString wfsServiceUrl() const;
void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const;

void describeFeatureType( const QString& aTypeName, QDomElement& parentElement, QDomDocument& doc ) const;

QStringList wfsLayers() const;
QSet<QString> wfsLayerSet() const;
int wfsLayerPrecision( const QString& aLayerId ) const;

QList<QgsMapLayer*> mapLayerFromTypeName( const QString& aTypeName, bool useCache = true ) const;

QSet<QString> wfstUpdateLayers() const;
QSet<QString> wfstInsertLayers() const;
QSet<QString> wfstDeleteLayers() const;

};
125 changes: 125 additions & 0 deletions python/server/qgswmsconfigparser.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/***************************************************************************
qgswmsconfigparser.sip
------------------------
begin : May 2, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/


class QgsWMSConfigParser
{
%TypeHeaderCode
#include "qgswmsconfigparser.h"

%End

public:

/**Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.
@param fullProjectInformation If true: add extended project information (does not validate against WMS schema)*/
virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc, const QString& version, bool fullProjectSettings = false ) const = 0;

/**Returns one or possibly several maplayers for a given layer name and style. If no layers/style are found, an empty list is returned*/
virtual QList<QgsMapLayer*> mapLayerFromStyle( const QString& lName, const QString& styleName, bool useCache = true ) const = 0;

/**Fills a layer and a style list. The two list have the same number of entries and the style and the layer at a position belong together (similar to the HTTP parameters 'Layers' and 'Styles'. Returns 0 in case of success*/
virtual int layersAndStyles( QStringList& layers, QStringList& styles ) const = 0;

/**Returns the xml fragment of a style*/
virtual QDomDocument getStyle( const QString& styleName, const QString& layerName ) const = 0;

/**Returns the xml fragment of layers styles*/
virtual QDomDocument getStyles( QStringList& layerList ) const = 0;

/**Returns the xml fragment of layers styles description*/
virtual QDomDocument describeLayer( QStringList& layerList, const QString& hrefString ) const = 0;

/**Returns if output are MM or PIXEL*/
virtual QgsMapRenderer::OutputUnits outputUnits() const = 0;

/**Returns an ID-list of layers which are not queryable (comes from <properties> -> <Identify> -> <disabledLayers in the project file*/
virtual QStringList identifyDisabledLayers() const = 0;

/**True if the feature info response should contain the wkt geometry for vector features*/
virtual bool featureInfoWithWktGeometry() const = 0;

/**Returns map with layer aliases for GetFeatureInfo (or 0 pointer if not supported). Key: layer name, Value: layer alias*/
virtual QHash<QString, QString> featureInfoLayerAliasMap() const = 0;

virtual QString featureInfoDocumentElement( const QString& defaultValue ) const = 0;

virtual QString featureInfoDocumentElementNS() const = 0;

virtual QString featureInfoSchema() const = 0;

/**Return feature info in format SIA2045?*/
virtual bool featureInfoFormatSIA2045() const = 0;

/**Draw text annotation items from the QGIS projectfile*/
virtual void drawOverlays( QPainter* p, int dpi, int width, int height ) const = 0;

/**Load PAL engine settings from the QGIS projectfile*/
virtual void loadLabelSettings( QgsLabelingEngineInterface* lbl ) const = 0;

virtual QString serviceUrl() const = 0;

virtual QStringList wfsLayerNames() const = 0;

virtual void owsGeneralAndResourceList( QDomElement& parentElement, QDomDocument& doc, const QString& strHref ) const = 0;

//legend
virtual double legendBoxSpace() const = 0;
virtual double legendLayerSpace() const = 0;
virtual double legendLayerTitleSpace() const = 0;
virtual double legendSymbolSpace() const = 0;
virtual double legendIconLabelSpace() const = 0;
virtual double legendSymbolWidth() const = 0;
virtual double legendSymbolHeight() const = 0;
virtual const QFont& legendLayerFont() const = 0;
virtual const QFont& legendItemFont() const = 0;

virtual double maxWidth() const = 0;
virtual double maxHeight() const = 0;
virtual double imageQuality() const = 0;

// WMS GetFeatureInfo precision (decimal places)
virtual int WMSPrecision() const = 0;

//printing

/**Creates a print composition, usually for a GetPrint request. Replaces map and label parameters*/
QgsComposition* createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const;

/**Creates a composition from the project file (probably delegated to the fallback parser)*/
//virtual QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList /Out/, QList< QgsComposerLegend* >& legendList /Out/, QList< QgsComposerLabel* >& labelList /Out/, QList<const QgsComposerHtml *>& htmlFrameList /Out/ ) const = 0;

/**Adds print capabilities to xml document. ParentElem usually is the <Capabilities> element*/
virtual void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;

virtual void setScaleDenominator( double denom ) = 0;
virtual void addExternalGMLData( const QString& layerName, QDomDocument* gmlDoc ) = 0;

virtual QList< QPair< QString, QgsLayerCoordinateTransform > > layerCoordinateTransforms() const = 0;

virtual int nLayers() const = 0;

virtual void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;

virtual bool useLayerIDs() const = 0;

private:

QgsWMSConfigParser();
virtual ~QgsWMSConfigParser();

};
111 changes: 111 additions & 0 deletions python/server/qgswmserver.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/***************************************************************************
qgswmsserver.sip
-------------------
begin : May 2, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsowsserver.h"
#include "qgswmsconfigparser.h"
#include <QDomDocument>
#include <QMap>
#include <QPair>
#include <QString>
#include <map>

class QgsCapabilitiesCache;
class QgsCoordinateReferenceSystem;
class QgsComposerLayerItem;
class QgsComposerLegendItem;
class QgsComposition;
class QgsConfigParser;
class QgsFeature;
class QgsFeatureRendererV2;
class QgsMapLayer;
class QgsMapRenderer;
class QgsPoint;
class QgsRasterLayer;
class QgsRasterRenderer;
class QgsRectangle;
class QgsRenderContext;
class QgsVectorLayer;
class QgsSymbol;
class QgsSymbolV2;
class QColor;
class QFile;
class QFont;
class QImage;
class QPaintDevice;
class QPainter;
class QStandardItem;

/**This class handles all the wms server requests. The parameters and values have to be passed in the form of
a map<QString, QString>. This map is usually generated by a subclass of QgsWMSRequestHandler, which makes QgsWMSServer
independent from any server side technology*/

class QgsWMSServer: public QgsOWSServer
{
public:
/**Constructor. Does _NOT_ take ownership of
QgsConfigParser, QgsCapabilitiesCache and QgsMapRenderer*/
QgsWMSServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWMSConfigParser* cp, QgsRequestHandler* rh,
QgsMapRenderer* renderer, QgsCapabilitiesCache* capCache );
~QgsWMSServer();

void executeRequest() override;

/**Returns an XML file with the capabilities description (as described in the WMS specs)
@param version WMS version (1.1.1 or 1.3.0)
@param fullProjectInformation If true: add extended project information (does not validate against WMS schema)*/
QDomDocument getCapabilities( QString version = "1.3.0", bool fullProjectInformation = false );

QDomDocument getContext();
/**Returns the map legend as an image (or a null pointer in case of error). The caller takes ownership
of the image object*/
QImage* getLegendGraphics();

typedef QSet<QgsSymbolV2*> SymbolV2Set;
typedef QMap<QgsVectorLayer*, SymbolV2Set> HitTest;

/**Returns the map as an image (or a null pointer in case of error). The caller takes ownership
of the image object). If an instance to existing hit test structure is passed, instead of rendering
it will fill the structure with symbols that would be used for rendering */
QImage* getMap( HitTest* hitTest = 0 );
/**Returns an SLD file with the style of the requested layer. Exception is raised in case of troubles :-)*/
QDomDocument getStyle();
/**Returns an SLD file with the styles of the requested layers. Exception is raised in case of troubles :-)*/
QDomDocument getStyles();
/**Returns a describeLayer file with the onlineResource of the requested layers. Exception is raised in case of troubles :-)*/
QDomDocument describeLayer();

/**Returns printed page as binary
@param formatString out: format of the print output (e.g. pdf, svg, png, ...)
@return printed page as binary or 0 in case of error*/
QByteArray* getPrint( const QString& formatString );

/**Creates an xml document that describes the result of the getFeatureInfo request.
@return 0 in case of success*/
int getFeatureInfo( QDomDocument& result, QString version = "1.3.0" );

/**Sets configuration parser for administration settings. Does not take ownership*/
void setAdminConfigParser( QgsWMSConfigParser* parser ) { mConfigParser = parser; }

/**Returns the schemaExtension for WMS 1.3.0 capabilities*/
QDomDocument getSchemaExtension();

private:
/**Don't use the default constructor*/
QgsWMSServer();

};

151 changes: 151 additions & 0 deletions python/server/qgswmsprojectparser.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/***************************************************************************
qgswcsprojectparser.sip
------------------------
begin : May 2, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/


class QgsWMSProjectParser : public QgsWMSConfigParser
{
%TypeHeaderCode
#include "qgswmsprojectparser.h"

%End
public:
QgsWMSProjectParser( const QString& filePath );
virtual ~QgsWMSProjectParser();

/**Adds layer and style specific capabilities elements to the parent node. This includes the individual layers and styles, their description, native CRS, bounding boxes, etc.
@param fullProjectInformation If true: add extended project information (does not validate against WMS schema)*/
void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc, const QString& version, bool fullProjectSettings = false ) const /*override*/ ;

QList<QgsMapLayer*> mapLayerFromStyle( const QString& lName, const QString& styleName, bool useCache = true ) const /*override*/ ;

QString serviceUrl() const /*override*/ ;

QStringList wfsLayerNames() const /*override*/ ;

void owsGeneralAndResourceList( QDomElement& parentElement, QDomDocument& doc, const QString& strHref ) const /*override*/ ;

//legend
double legendBoxSpace() const /*override*/ ;
double legendLayerSpace() const /*override*/ ;
double legendLayerTitleSpace() const /*override*/ ;
double legendSymbolSpace() const /*override*/ ;
double legendIconLabelSpace() const /*override*/ ;
double legendSymbolWidth() const /*override*/ ;
double legendSymbolHeight() const /*override*/ ;
const QFont& legendLayerFont() const /*override*/ ;
const QFont& legendItemFont() const /*override*/ ;

double maxWidth() const /*override*/ ;
double maxHeight() const /*override*/ ;
double imageQuality() const /*override*/ ;
int WMSPrecision() const /*override*/ ;

//printing
//QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap* >& mapList, //QList< QgsComposerLegend* >& legendList, QList< QgsComposerLabel* >& labelList, QList<const QgsComposerHtml *>& htmlFrameList ) const /*override*/ ;

void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const /*override*/ ;

//todo: fixme
void setScaleDenominator( double ) /*override*/;
void addExternalGMLData( const QString&, QDomDocument* ) /*override*/ ;

QList< QPair< QString, QgsLayerCoordinateTransform > > layerCoordinateTransforms() const /*override*/ ;

/**Fills a layer and a style list. The two list have the same number of entries and the style and the layer at a position belong together (similar to the HTTP parameters 'Layers' and 'Styles'. Returns 0 in case of success*/
int layersAndStyles( QStringList& layers, QStringList& styles ) const /*override*/ ;

/**Returns the xml fragment of a style*/
QDomDocument getStyle( const QString& styleName, const QString& layerName ) const /*override*/ ;

/**Returns the xml fragment of layers styles*/
QDomDocument getStyles( QStringList& layerList ) const /*override*/ ;

/**Returns the xml fragment of layers styles description*/
QDomDocument describeLayer( QStringList& layerList, const QString& hrefString ) const /*override*/ ;

/**Returns if output are MM or PIXEL*/
QgsMapRenderer::OutputUnits outputUnits() const /*override*/ ;

/**True if the feature info response should contain the wkt geometry for vector features*/
bool featureInfoWithWktGeometry() const /*override*/ ;

/**Returns map with layer aliases for GetFeatureInfo (or 0 pointer if not supported). Key: layer name, Value: layer alias*/
QHash<QString, QString> featureInfoLayerAliasMap() const /*override*/ ;

QString featureInfoDocumentElement( const QString& defaultValue ) const /*override*/ ;

QString featureInfoDocumentElementNS() const /*override*/ ;

QString featureInfoSchema() const /*override*/ ;

/**Return feature info in format SIA2045?*/
bool featureInfoFormatSIA2045() const /*override*/ ;

/**Draw text annotation items from the QGIS projectfile*/
void drawOverlays( QPainter* p, int dpi, int width, int height ) const /*override*/ ;

/**Load PAL engine settings from projectfile*/
void loadLabelSettings( QgsLabelingEngineInterface* lbl ) const /*override*/ ;

int nLayers() const /*override*/ ;

void serviceCapabilities( QDomElement& parentElement, QDomDocument& doc ) const /*override*/ ;

bool useLayerIDs() const /*override*/ ;

private:

/**Returns an ID-list of layers which are not queryable (comes from <properties> -> <Identify> -> <disabledLayers in the project file*/
virtual QStringList identifyDisabledLayers() const /*override*/ ;

/**Reads layer drawing order from the legend section of the project file and appends it to the parent elemen (usually the <Capability> element)*/
void addDrawingOrder( QDomElement& parentElem, QDomDocument& doc, const QHash<QString, QString> &idNameMap, const QStringList &layerIDList ) const;

void addLayerStyles( QgsMapLayer* currentLayer, QDomDocument& doc, QDomElement& layerElem, const QString& version ) const;

void addLayers( QDomDocument &doc,
QDomElement &parentLayer,
const QDomElement &legendElem,
const QMap<QString, QgsMapLayer *> &layerMap,
const QStringList &nonIdentifiableLayers,
QString version, //1.1.1 or 1.3.0
bool fullProjectSettings,
QHash<QString, QString> &idNameMap,
QStringList &layerIDList ) const;

void addOWSLayerStyles( QgsMapLayer* currentLayer, QDomDocument& doc, QDomElement& layerElem ) const;

void addOWSLayers( QDomDocument &doc, QDomElement &parentElem, const QDomElement &legendElem,
const QMap<QString, QgsMapLayer *> &layerMap, const QStringList &nonIdentifiableLayers,
const QString& strHref, QgsRectangle& combinedBBox, QString strGroup ) const;

/**Adds layers from a legend group to list (could be embedded or a normal group)*/
//void addLayersFromGroup( const QDomElement& legendGroupElem, QMap< int, QgsMapLayer*>& layers, bool useCache = true ) const;

QDomElement composerByName( const QString& composerName ) const;
QgsLayerTreeGroup* projectLayerTreeGroup() const;

static bool annotationPosition( const QDomElement& elem, double scaleFactor, double& xPos, double& yPos );
static void drawAnnotationRectangle( QPainter* p, const QDomElement& elem, double scaleFactor, double xPos, double yPos, int itemWidth, int itemHeight );
void createTextAnnotationItems();
void createSvgAnnotationItems();
void cleanupSvgAnnotationItems();
void cleanupTextAnnotationItems();

QString getCapaServiceUrl( QDomDocument& doc ) const;
};

14 changes: 13 additions & 1 deletion python/server/server.sip
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@

%Import core/core.sip

%Feature HAVE_SERVER_PYTHON_PLUGINS

%If (HAVE_SERVER_PYTHON_PLUGINS)
%Include qgsserverfilter.sip
%Include qgsserverinterface.sip
%End

%Include qgsmapserviceexception.sip
%Include qgscapabilitiescache.sip
%Include qgsrequesthandler.sip
%Include qgsserverinterface.sip
%Include qgsserverprojectparser.sip
%Include qgswcsprojectparser.sip
%Include qgswmsconfigparser.sip
%Include qgswmsprojectparser.sip
%Include qgswfsprojectparser.sip
%Include qgsconfigcache.sip
%Include qgsserver.sip
15 changes: 12 additions & 3 deletions src/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ SET ( qgis_mapserv_SRCS
qgsserverprojectparser.cpp
qgssldconfigparser.cpp
qgsconfigparserutils.cpp
qgsserver.cpp
)

# SET (qgis_mapserv_UIS
Expand Down Expand Up @@ -74,12 +75,15 @@ SET (qgis_mapserv_RCCS

SET(qgis_mapserv_MOC_HDRS ${qgis_mapserv_MOC_HDRS})


IF (WITH_SERVER_PLUGINS)
SET(qgis_mapserv_SRCS ${qgis_mapserv_SRCS}
qgsserverplugins.cpp
qgsserverinterface.cpp
qgsserverfilter.cpp
qgsserverinterfaceimpl.cpp
)
ENDIF (WITH_SERVER_PLUGINS)

IF (NOT MSVC)
ADD_DEFINITIONS("-USERVER_EXPORT")
Expand All @@ -106,18 +110,23 @@ TARGET_LINK_LIBRARIES(qgis_server
qgis_core
qgis_gui
qgis_analysis
qgispython
${PROJ_LIBRARY}
${FCGI_LIBRARY}
${POSTGRES_LIBRARY}
${GDAL_LIBRARY}
)

IF (WITH_BINDINGS)
TARGET_LINK_LIBRARIES(qgis_server
qgispython
)
ENDIF(WITH_BINDINGS)

IF (APPLE)
SET_TARGET_PROPERTIES(qgis_server PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
ENDIF (APPLE)

IF (WITH_SERVER_PLUGINS)

# install

INSTALL(TARGETS qgis_server
Expand All @@ -131,7 +140,7 @@ INCLUDE_DIRECTORIES(
../python
)

ENDIF (WITH_SERVER_PLUGINS) # end qgis_server library
# end qgis_server library

QT4_WRAP_UI (qgis_mapserv_UIS_H ${qgis_mapserv_UIS})

Expand Down
432 changes: 6 additions & 426 deletions src/server/qgis_map_serv.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/server/qgsconfigcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class QgsWMSConfigParser;

class QDomDocument;

class QgsConfigCache: public QObject
class SERVER_EXPORT QgsConfigCache : public QObject
{
Q_OBJECT
public:
Expand Down
4 changes: 2 additions & 2 deletions src/server/qgsgetrequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
#include <QUrl>
#include <stdlib.h>

QgsGetRequestHandler::QgsGetRequestHandler()
: QgsHttpRequestHandler()
QgsGetRequestHandler::QgsGetRequestHandler( const bool captureOutput /*= FALSE*/ )
: QgsHttpRequestHandler( captureOutput )
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/server/qgsgetrequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class QgsGetRequestHandler: public QgsHttpRequestHandler
{
public:
QgsGetRequestHandler();
QgsGetRequestHandler( const bool captureOutput = FALSE );
void parseInput() override;
};

Expand Down
91 changes: 70 additions & 21 deletions src/server/qgshttprequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@
#include <QUrl>
#include <fcgi_stdio.h>

QgsHttpRequestHandler::QgsHttpRequestHandler()
: QgsRequestHandler()

QgsHttpRequestHandler::QgsHttpRequestHandler( const bool captureOutput /*= FALSE*/ )
: QgsRequestHandler( )
{
mException = NULL;
mHeadersSent = FALSE;
mCaptureOutput = captureOutput;
}

QgsHttpRequestHandler::~QgsHttpRequestHandler()
{

}

void QgsHttpRequestHandler::setHttpResponse( QByteArray *ba, const QString &format )
Expand Down Expand Up @@ -103,49 +104,81 @@ void QgsHttpRequestHandler::setInfoFormat( const QString &format )
mInfoFormat = format;
}

void QgsHttpRequestHandler::addToResponseHeader( const char * response )
{
if ( mCaptureOutput )
{
mResponseHeader.append( response );
}
else
{
printf( response );
}
}

void QgsHttpRequestHandler::addToResponseBody( const char * response )
{
if ( mCaptureOutput )
{
mResponseBody.append( response );
}
else
{
printf( response );
}
}

void QgsHttpRequestHandler::sendHeaders()
{
// Send default headers if they've not been set in a previous stage
if ( mHeaders.empty() )
{
QgsDebugMsg( QString( "Content size: %1" ).arg( mBody.size() ) );
QgsDebugMsg( QString( "Content format: %1" ).arg( mInfoFormat ) );
printf( "Content-Type: " );
printf( mInfoFormat.toLocal8Bit() );
addToResponseHeader( "Content-Type: " );
addToResponseHeader( mInfoFormat.toUtf8() );
if ( mInfoFormat.startsWith( "text/" ) )
printf( "; charset=utf-8" );
printf( "\n" );
addToResponseHeader( "; charset=utf-8" );
addToResponseHeader( "\n" );
// size is not known when streaming
if ( mBody.size() > 0 )
{
printf( "Content-Length: %d\n", mBody.size() );
addToResponseHeader( QString( "Content-Length: %1\n" ).arg( mBody.size() ).toUtf8( ) );
}
}
else
{
QMap<QString, QString>::const_iterator it;
for ( it = mHeaders.constBegin(); it != mHeaders.constEnd(); ++it )
{
printf( it.key().toLocal8Bit() );
printf( ": " );
printf( it.value().toLocal8Bit() );
printf( "\n" );
addToResponseHeader( it.key().toUtf8() );
addToResponseHeader( ": " );
addToResponseHeader( it.value().toUtf8() );
addToResponseHeader( "\n" );
}
printf( "\n" );
addToResponseHeader( "\n" );
}
printf( "\n" );
addToResponseHeader( "\n" );
mHeaders.clear();
mHeadersSent = TRUE;
}

void QgsHttpRequestHandler::sendBody() const
void QgsHttpRequestHandler::sendBody()
{
size_t result = fwrite(( void* )mBody.data(), mBody.size(), 1, FCGI_stdout );
if ( mCaptureOutput )
{
mResponseBody.append( mBody );
}
else
{
// Cannot use addToResponse because it uses printf
size_t result = fwrite(( void* )mBody.data(), mBody.size(), 1, FCGI_stdout );
#ifdef QGISDEBUG
QgsDebugMsg( QString( "Sent %1 blocks of %2 bytes" ).arg( result ).arg( mBody.size() ) );
QgsDebugMsg( QString( "Sent %1 blocks of %2 bytes" ).arg( result ).arg( mBody.size() ) );
#else
Q_UNUSED( result );
Q_UNUSED( result );
#endif
}
}

#ifdef HAVE_SERVER_PYTHON_PLUGINS
Expand Down Expand Up @@ -181,6 +214,22 @@ void QgsHttpRequestHandler::sendResponse()
clearBody();
}

QByteArray QgsHttpRequestHandler::getResponse( const bool returnHeaders,
const bool returnBody )
{
if ( ! returnHeaders )
{
return mResponseBody;
}
else if ( ! returnBody )
{
return mResponseHeader;
}
else
{
return mResponseHeader.append( mResponseBody );
}
}

QString QgsHttpRequestHandler::formatToMimeType( const QString& format ) const
{
Expand Down Expand Up @@ -253,7 +302,7 @@ void QgsHttpRequestHandler::setGetMapResponse( const QString& service, QImage* i
}
else
{
img->save( &buffer, mFormat.toLocal8Bit().data(), imageQuality );
img->save( &buffer, mFormat.toUtf8().data(), imageQuality );
}

if ( isBase64 )
Expand Down Expand Up @@ -515,11 +564,11 @@ void QgsHttpRequestHandler::requestStringToParameterMap( const QString& request,
}

QString key = element.left( sepidx );
key = QUrl::fromPercentEncoding( key.toLocal8Bit() ); //replace encoded special characters and utf-8 encodings
key = QUrl::fromPercentEncoding( key.toUtf8() ); //replace encoded special characters and utf-8 encodings

QString value = element.mid( sepidx + 1 );
value.replace( "+", " " );
value = QUrl::fromPercentEncoding( value.toLocal8Bit() ); //replace encoded special characters and utf-8 encodings
value = QUrl::fromPercentEncoding( value.toUtf8() ); //replace encoded special characters and utf-8 encodings

if ( key.compare( "SLD_BODY", Qt::CaseInsensitive ) == 0 )
{
Expand Down
17 changes: 15 additions & 2 deletions src/server/qgshttprequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ It provides a method to set data to the client*/
class QgsHttpRequestHandler: public QgsRequestHandler
{
public:
QgsHttpRequestHandler();
QgsHttpRequestHandler( const bool captureOutput /*= FALSE*/ );
~QgsHttpRequestHandler();

virtual void setGetMapResponse( const QString& service, QImage* img, int imageQuality ) override;
Expand Down Expand Up @@ -62,9 +62,16 @@ class QgsHttpRequestHandler: public QgsRequestHandler
#ifdef HAVE_SERVER_PYTHON_PLUGINS
virtual void setPluginFilters( QgsServerFiltersMap pluginFilters ) override;
#endif
// TODO: if HAVE_SERVER_PYTHON
QByteArray getResponseHeader( ) { return mResponseHeader; }
QByteArray getResponseBody( ) { return mResponseBody; }
/** Return the response if capture output is activated */
QByteArray getResponse( const bool returnHeaders = TRUE,
const bool returnBody = TRUE );

protected:
virtual void sendHeaders( ) override;
virtual void sendBody( ) const override;
virtual void sendBody( ) override;
void setHttpResponse( QByteArray *ba, const QString &format );
/**Converts format to official mimetype (e.g. 'jpg' to 'image/jpeg')
@return mime string (or the entered string if not found)*/
Expand All @@ -86,6 +93,12 @@ class QgsHttpRequestHandler: public QgsRequestHandler
static bool alphaCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 );
/**Calculates a representative color for a box (pixel weighted average)*/
static QRgb boxColor( const QgsColorBox& box, int boxPixels );
// TODO: if HAVE_SERVER_PYTHON
QByteArray mResponseHeader;
QByteArray mResponseBody;
bool mCaptureOutput;
void addToResponseHeader( const char * response );
void addToResponseBody( const char * response );
};

#endif
4 changes: 3 additions & 1 deletion src/server/qgspostrequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#include "qgslogger.h"
#include <QDomDocument>

QgsPostRequestHandler::QgsPostRequestHandler()
QgsPostRequestHandler::QgsPostRequestHandler( const bool captureOutput /*= FALSE*/ )
: QgsHttpRequestHandler( captureOutput )
{

}

QgsPostRequestHandler::~QgsPostRequestHandler()
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgspostrequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
class QgsPostRequestHandler: public QgsHttpRequestHandler
{
public:
QgsPostRequestHandler();
QgsPostRequestHandler( const bool captureOutput = FALSE );
~QgsPostRequestHandler();

/**Parses the input and creates a request neutral Parameter/Value map*/
Expand Down
18 changes: 14 additions & 4 deletions src/server/qgsrequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ class QgsRequestHandler

public:

QgsRequestHandler()
QgsRequestHandler( )
: mHeadersSent( false )
, mException( 0 )
{}
virtual ~QgsRequestHandler() {}
virtual ~QgsRequestHandler( ) {}
/**Parses the input and creates a request neutral Parameter/Value map*/
virtual void parseInput() = 0;
/**Sends the map image back to the client*/
Expand Down Expand Up @@ -105,9 +105,15 @@ class QgsRequestHandler
/**Allow core services to call plugin hooks through sendResponse() */
virtual void setPluginFilters( QgsServerFiltersMap pluginFilters ) = 0;
#endif
// TODO: if HAVE_SERVER_PYTHON
virtual QByteArray getResponseHeader( ) = 0;
virtual QByteArray getResponseBody( ) = 0;
virtual QByteArray getResponse( const bool returnHeaders = TRUE,
const bool returnBody = TRUE ) = 0;

protected:
virtual void sendHeaders( ) = 0;
virtual void sendBody( ) const = 0;
virtual void sendBody( ) = 0;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsServerFiltersMap mPluginFilters;
#endif
Expand All @@ -123,7 +129,11 @@ class QgsRequestHandler
/** Response headers. They can be empty, in this case headers are
automatically generated from the content mFormat */
QMap<QString, QString> mHeaders;

// TODO: if HAVE_SERVER_PYTHON
/** Response output buffers, used by Python bindings to return
* output instead of printing with fcgi printf */
QByteArray mResponseHeader;
QByteArray mResponseBody;
};

#endif
588 changes: 588 additions & 0 deletions src/server/qgsserver.cpp

Large diffs are not rendered by default.

134 changes: 134 additions & 0 deletions src/server/qgsserver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/***************************************************************************
qgsserver.h
QGIS Server main class.
-------------------
begin : June 05, 2015
copyright : (C) 2015 by Alessandro Pasotti
email : a dot pasotti at itopen dot it
Based on previous work from:
begin : July 04, 2006
copyright : (C) 2006 by Marco Hugentobler & Ionut Iosifescu Enescu
email : marco dot hugentobler at karto dot baug dot ethz dot ch
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/


#ifndef QGSSERVER_H
#define QGSSERVER_H

#include <QFileInfo>
#include "qgsrequesthandler.h"
#include "qgsapplication.h"
#include "qgsmaprenderer.h"
#include "qgsconfigcache.h"
#include "qgscapabilitiescache.h"

#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsserverplugins.h"
#include "qgsserverfilter.h"
#include "qgsserverinterfaceimpl.h"
#endif


/**
* The QgsServer class provides OGC web services.
*/
class SERVER_EXPORT QgsServer
{
public:
QgsServer();
~QgsServer();
/** Server initialisation: intialise QGIS ang QT core application.
* This method is automatically called by handleRequest if it wasn't
* explicitly called before */
static bool init( int & argc, char ** argv );
//! The following is mainly for python bindings, that do not pass argc/argv
static bool init();

/** Handle the request. The output is normally printed trough FCGI printf
* by the request handler or, in case the server has been invoked from python
* bindings, a flag is set that capures all the output headers and body, instead
* of printing it returns the output as a QByteArray.
* When calling handleRequest() from python bindings an additional argument
* specify if we only want the headers or the body back, this is mainly useful
* for testing purposes.
* The query string is normally read from environment
* but can be also passed in args and in this case overrides the environment
* variable
*
* @param queryString optional QString containing the query string
* @return the response QByteArray if called from python bindings, empty otherwise
*/
QByteArray handleRequest( const QString queryString = QString( ) );
QByteArray handleRequest( const QString queryString,
const bool returnHeaders,
const bool returnBody );
/**
* Handles the request and returns only the body
*
* @param queryString optional QString containing the query string
* @return the response body QByteArray if called from python bindings, empty otherwise
*/
QByteArray handleRequestGetBody( const QString queryString = QString( ) );

/**
* Handles the request and returns only the headers
*
* @param queryString optional QString containing the query string
* @return the response headers QByteArray if called from python bindings, empty otherwise
*/
QByteArray handleRequestGetHeaders( const QString queryString = QString( ) );

/** Returns a pointer to the server interface */
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsServerInterfaceImpl* serverInterface( ) { return mServerInterface; }
#endif

private:
// All functions that where previously in the main file are now
// static methods of this class
static QString configPath( const QString& defaultConfigPath,
const QMap<QString, QString>& parameters );
// Mainly for debug
static void dummyMessageHandler( QtMsgType type, const char *msg );
// Mainly for debug
static void printRequestInfos();
// Mainly for debug
static void printRequestParameters(
const QMap< QString, QString>& parameterMap,
int logLevel );
static QFileInfo defaultProjectFile();
static QFileInfo defaultAdminSLD();
static void setupNetworkAccessManager();
//! Create and return a request handler instance
static QgsRequestHandler* createRequestHandler(
const bool captureOutput = FALSE );

// Server status
static QString mConfigFilePath;
static QgsCapabilitiesCache* mCapabilitiesCache;
static QgsMapRenderer* mMapRenderer;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
static QgsServerInterfaceImpl* mServerInterface;
static bool mInitPython;
#endif
static bool mInitialised;
static QString mServerName;
static char* mArgv[1];
static int mArgc;
static QgsApplication* mQgsApplication;
static bool mCaptureOutput;
};
#endif // QGSSERVER_H

4 changes: 2 additions & 2 deletions src/server/qgsserverfilter.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
qgsseerverfilter.cpp
Server I/O filters class for Qgis Mapserver for use by plugins
Server I/O filters class for QGIS Server for use by plugins
-------------------
begin : 2014-09-10
copyright : (C) 2014 by Alessandro Pasotti
Expand All @@ -22,7 +22,7 @@

/**
* QgsServerFilter
* Class defining I/O filters for Qgis Mapserver and
* Class defining I/O filters for QGIS Server and
* implemented in plugins
*
*/
Expand Down
6 changes: 3 additions & 3 deletions src/server/qgsserverfilter.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
qgsserverfilter.h
Server I/O filters class for Qgis Mapserver for use by plugins
Server I/O filters class for QGIS Server for use by plugins
-------------------
begin : 2014-09-10
copyright : (C) 2014 by Alessandro Pasotti
Expand All @@ -16,6 +16,7 @@
* *
***************************************************************************/


#ifndef QGSSERVERFILTER_H
#define QGSSERVERFILTER_H

Expand All @@ -25,7 +26,7 @@ class QgsServerInterface;

/**
* \class QgsServerFilter
* \brief Class defining I/O filters for Qgis Mapserver and
* \brief Class defining I/O filters for QGIS Server and
* implemented in plugins.
*
* Filters can define any (or none) of the following hooks:
Expand Down Expand Up @@ -73,5 +74,4 @@ class SERVER_EXPORT QgsServerFilter

typedef QMultiMap<int, QgsServerFilter*> QgsServerFiltersMap;


#endif // QGSSERVERFILTER_H
3 changes: 2 additions & 1 deletion src/server/qgsserverinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

#include "qgsserverinterface.h"

QgsServerInterface::QgsServerInterface()
QgsServerInterface::QgsServerInterface():
mConfigFilePath( QString() )
{


Expand Down
21 changes: 18 additions & 3 deletions src/server/qgsserverinterface.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
qgsseerversinterface.h
Interface class for exposing functions in Qgis Server for use by plugins
Interface class for exposing functions in QGIS Server for use by plugins
-------------------
begin : 2014-09-10
copyright : (C) 2014 by Alessandro Pasotti
Expand All @@ -16,6 +16,7 @@
* *
***************************************************************************/


#ifndef QGSSERVERINTERFACE_H
#define QGSSERVERINTERFACE_H

Expand All @@ -25,11 +26,10 @@

/**
* QgsServerInterface
* Class defining interfaces exposed by Qgis Mapserver and
* Class defining interfaces exposed by QGIS Server and
* made available to plugins.
*
*/

class SERVER_EXPORT QgsServerInterface
{

Expand Down Expand Up @@ -74,6 +74,21 @@ class SERVER_EXPORT QgsServerInterface

//! Return an enrironment variable, used to pass environment variables to python
virtual QString getEnv( const QString& name ) const = 0;

/**
* Return the configuration file path
* @return QString containing the configuration file path
*/
virtual QString configFilePath( ) = 0;

/**
* Set the configuration file path
* @param configFilePath QString with the configuration file path
*/
virtual void setConfigFilePath( QString configFilePath ) = 0;

private:
QString mConfigFilePath;
};

#endif // QGSSERVERINTERFACE_H
7 changes: 6 additions & 1 deletion src/server/qgsserverinterfaceimpl.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
qgsseerversinterface.h
Interface class for exposing functions in Qgis Mapserver for use by plugins
Interface class for exposing functions in QGIS Server for use by plugins
-------------------
begin : 2014-09-10
copyright : (C) 2014 by Alessandro Pasotti
Expand Down Expand Up @@ -43,6 +43,11 @@ void QgsServerInterfaceImpl::setRequestHandler( QgsRequestHandler * requestHandl
mRequestHandler = requestHandler;
}

void QgsServerInterfaceImpl::setConfigFilePath( QString configFilePath )
{
mConfigFilePath = configFilePath;
}

void QgsServerInterfaceImpl::registerFilter( QgsServerFilter *filter, int priority )
{
mFilters.insert( priority, filter );
Expand Down
7 changes: 5 additions & 2 deletions src/server/qgsserverinterfaceimpl.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
qgsseerversinterface.h
Interface class for exposing functions in Qgis Mapserver for use by plugins
Interface class for exposing functions in QGIS Server for use by plugins
-------------------
begin : 2014-09-10
copyright : (C) 2014 by Alessandro Pasotti
Expand Down Expand Up @@ -28,7 +28,7 @@

/**
* QgsServerInterface
* Class defining interfaces exposed by Qgis Mapserver and
* Class defining interfaces exposed by QGIS Server and
* made available to plugins.
*
*/
Expand All @@ -50,9 +50,12 @@ class QgsServerInterfaceImpl : public QgsServerInterface
void registerFilter( QgsServerFilter *filter, int priority = 0 ) override;
QgsServerFiltersMap filters( ) override { return mFilters; }
QString getEnv( const QString& name ) const override;
QString configFilePath( ) override { return mConfigFilePath; }
void setConfigFilePath( QString configFilePath );

private:

QString mConfigFilePath;
QgsServerFiltersMap mFilters;
QgsCapabilitiesCache* mCapabilitiesCache;
QgsRequestHandler* mRequestHandler;
Expand Down
3 changes: 3 additions & 0 deletions src/server/qgsserverplugins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
* *
***************************************************************************/


#include "qgsserverplugins.h"
#include "qgsmapserviceexception.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgspythonutils.h"
#include "qgsserverlogger.h"
#include "qgsmsutils.h"

Expand Down Expand Up @@ -115,3 +117,4 @@ bool QgsServerPlugins::initPlugins( QgsServerInterface *interface )
}
return mPythonUtils && mPythonUtils->isEnabled() && atLeastOneEnabled;
}

4 changes: 3 additions & 1 deletion src/server/qgsserverplugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
#define QGSSERVERPLUGINS_H

#include "qgsrequesthandler.h"
#include "qgspythonutils.h"
#include "qgsserverinterface.h"

// This is needed by SIP otherwise it doesn't find QgsPythonUtils header
class QgsPythonUtils;

class SERVER_EXPORT QgsServerPlugins
{
public:
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgsserverprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class QgsMapLayer;
class QgsRectangle;
class QDomDocument;

class QgsServerProjectParser
class SERVER_EXPORT QgsServerProjectParser
{
public:

Expand Down
5 changes: 2 additions & 3 deletions src/server/qgssoaprequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@
#include <time.h>
#include <fcgi_stdio.h>

QgsSOAPRequestHandler::QgsSOAPRequestHandler()
QgsSOAPRequestHandler::QgsSOAPRequestHandler( const bool captureOutput /*= FALSE*/ )
: QgsHttpRequestHandler( captureOutput )
{

}

QgsSOAPRequestHandler::~QgsSOAPRequestHandler()
{

}

void QgsSOAPRequestHandler::parseInput()
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgssoaprequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class QDomElement;
class QgsSOAPRequestHandler: public QgsHttpRequestHandler
{
public:
QgsSOAPRequestHandler();
QgsSOAPRequestHandler( const bool captureOutput = FALSE );
~QgsSOAPRequestHandler();
void parseInput() override;
void setGetMapResponse( const QString& service, QImage* img );
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgswcsprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

#include "qgsserverprojectparser.h"

class QgsWCSProjectParser
class SERVER_EXPORT QgsWCSProjectParser
{
public:
QgsWCSProjectParser( const QString& filePath );
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgswfsprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

#include "qgsserverprojectparser.h"

class QgsWFSProjectParser
class SERVER_EXPORT QgsWFSProjectParser
{
public:
QgsWFSProjectParser( const QString& filePath );
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgswmsconfigparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class QgsComposition;
class QgsMapLayer;


class QgsWMSConfigParser
class SERVER_EXPORT QgsWMSConfigParser
{
public:

Expand Down
2 changes: 1 addition & 1 deletion src/server/qgswmsprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class QTextDocument;
class QSvgRenderer;

class QgsWMSProjectParser : public QgsWMSConfigParser
class SERVER_EXPORT QgsWMSProjectParser : public QgsWMSConfigParser
{
public:
QgsWMSProjectParser( const QString& filePath );
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgswmsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1745,7 +1745,7 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
return 0;
}

//apply DPI parameter if present. This is an extension of QGIS mapserver compared to WMS 1.3.
//apply DPI parameter if present. This is an extension of Qgis Mapserver compared to WMS 1.3.
//Because of backwards compatibility, this parameter is optional
double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis
int dpm = 1 / OGC_PX_M;
Expand Down
3 changes: 3 additions & 0 deletions tests/src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ ENDIF (ENABLE_PGTEST)
IF (WITH_APIDOC)
ADD_PYTHON_TEST(PyQgsDocCoverage test_qgsdoccoverage.py)
ENDIF (WITH_APIDOC)
IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
ENDIF (WITH_SERVER)
121 changes: 121 additions & 0 deletions tests/src/python/test_qgsserver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsServer.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Alessandro Pasotti'
__date__ = '25/05/2015'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

import os
import re
import unittest
from qgis.server import QgsServer
from qgis.core import QgsMessageLog
from utilities import unitTestDataPath

# Strip path and content lenght because path may vary
RE_STRIP_PATH=r'MAP=[^&]+|Content-Length: \d+'


class TestQgsServer(unittest.TestCase):

def setUp(self):
"""Create the server instance"""
self.testdata_path = unitTestDataPath('qgis_server') + '/'
# Clean env just to be sure
env_vars = ['QUERY_STRING', 'QGIS_PROJECT_FILE']
for ev in env_vars:
try:
del os.environ[ev]
except KeyError:
pass
self.server = QgsServer()


def test_destructor_segfaults(self):
"""Segfault on destructor?"""
server = QgsServer()
del server


def test_multiple_servers(self):
"""Segfaults?"""
for i in range(10):
locals()["s%s" % i] = QgsServer()
locals()["s%s" % i].handleRequest()


def test_api(self):
"""Using an empty query string (returns an XML exception)
we are going to test if headers and body are returned correctly"""
# Test as a whole
response = str(self.server.handleRequest())
expected = 'Content-Type: text/xml; charset=utf-8\nContent-Length: 206\n\n<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc">\n <ServiceException code="Service configuration error">Service unknown or unsupported</ServiceException>\n</ServiceExceptionReport>\n'
self.assertEqual(response, expected)
# Test header
response = str(self.server.handleRequestGetHeaders())
expected = 'Content-Type: text/xml; charset=utf-8\nContent-Length: 206\n\n'
self.assertEqual(response, expected)
# Test body
response = str(self.server.handleRequestGetBody())
expected = '<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc">\n <ServiceException code="Service configuration error">Service unknown or unsupported</ServiceException>\n</ServiceExceptionReport>\n'
self.assertEqual(response, expected)


def test_pluginfilters(self):
"""Test python plugins filters"""
try:
from qgis.server import QgsServerFilter
except ImportError:
print "QGIS Server plugins are not compiled. Skipping test"
return

class SimpleHelloFilter(QgsServerFilter):
def requestReady(self):
QgsMessageLog.logMessage("SimpleHelloFilter.requestReady")

def sendResponse(self):
QgsMessageLog.logMessage("SimpleHelloFilter.sendResponse")

def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
QgsMessageLog.logMessage("SimpleHelloFilter.responseComplete")
if params.get('SERVICE', '').upper() == 'SIMPLE':
request.clearHeaders()
request.setHeader('Content-type', 'text/plain')
request.clearBody()
request.appendBody('Hello from SimpleServer!')


serverIface = self.server.serverInterface()
serverIface.registerFilter(SimpleHelloFilter(serverIface), 100 )
response = str(self.server.handleRequest('service=simple'))
expected = 'Content-type: text/plain\n\n\nHello from SimpleServer!'
self.assertEqual(response, expected)


## WMS tests
def wms_request_compare(self, request):
map = self.testdata_path + "testproject.qgs"
response = str(self.server.handleRequest('MAP=%s&SERVICE=WMS&VERSION=1.3&REQUEST=%s' % (map, request)))
f = open(self.testdata_path + request.lower() + '.txt')
expected = f.read()
f.close()
self.assertEqual(re.sub(RE_STRIP_PATH, '', response), re.sub(RE_STRIP_PATH, '', expected))


def test_project_wms(self):
"""Test some WMS request"""
for request in ('GetCapabilities', 'GetProjectSettings'):
self.wms_request_compare(request)


if __name__ == '__main__':
unittest.main()
133 changes: 133 additions & 0 deletions tests/testdata/qgis_server/getcapabilities.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
Content-Type: text/xml; charset=utf-8
Content-Length: 5213

<?xml version="1.0" encoding="utf-8"?>
<WMS_Capabilities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3" xmlns="http://www.opengis.net/wms" xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd http://www.qgis.org/wms http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;SERVICE=WMS&amp;REQUEST=GetSchemaExtension" xmlns:sld="http://www.opengis.net/sld" xmlns:qgs="http://www.qgis.org/wms">
<Service>
<Name>WMS</Name>
<Title>QGIS TestProject</Title>
<Abstract>Some UTF8 text èòù</Abstract>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href=""/>
<ContactInformation>
<ContactPersonPrimary>
<ContactPerson>Alessandro Pasotti</ContactPerson>
<ContactOrganization>QGIS dev team</ContactOrganization>
</ContactPersonPrimary>
<ContactVoiceTelephone></ContactVoiceTelephone>
<ContactElectronicMailAddress>elpaso@itopen.it</ContactElectronicMailAddress>
</ContactInformation>
<Fees></Fees>
<AccessConstraints></AccessConstraints>
</Service>
<Capability>
<Request>
<GetCapabilities>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetCapabilities>
<GetMap>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>image/png; mode=16bit</Format>
<Format>image/png; mode=8bit</Format>
<Format>image/png; mode=1bit</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetMap>
<GetFeatureInfo>
<Format>text/plain</Format>
<Format>text/html</Format>
<Format>text/xml</Format>
<Format>application/vnd.ogc.gml</Format>
<Format>application/vnd.ogc.gml/3.1.1</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetFeatureInfo>
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</sld:GetLegendGraphic>
<sld:DescribeLayer>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</sld:DescribeLayer>
<qgs:GetStyles>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</qgs:GetStyles>
</Request>
<Exception>
<Format>text/xml</Format>
</Exception>
<Layer queryable="1">
<Name>QGIS Test Project</Name>
<Title>QGIS Test Project</Title>
<CRS>EPSG:4326</CRS>
<CRS>EPSG:3857</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>8.20315</westBoundLongitude>
<eastBoundLongitude>8.20416</eastBoundLongitude>
<southBoundLatitude>44.9012</southBoundLatitude>
<northBoundLatitude>44.9016</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="EPSG:4326" maxx="44.9016" minx="44.9012" maxy="8.20416" miny="8.20315"/>
<Layer queryable="1">
<Name>testlayer èé</Name>
<Title>A test vector layer</Title>
<Abstract>A test vector layer with unicode òà</Abstract>
<CRS>EPSG:4326</CRS>
<CRS>EPSG:3857</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>8.20346</westBoundLongitude>
<eastBoundLongitude>8.20355</eastBoundLongitude>
<southBoundLatitude>44.9014</southBoundLatitude>
<northBoundLatitude>44.9015</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="EPSG:4326" maxx="44.9015" minx="44.9014" maxy="8.20355" miny="8.20346"/>
<Style>
<Name>default</Name>
<Title>default</Title>
<LegendURL>
<Format>image/png</Format>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;&amp;SERVICE=WMS&amp;VERSION=1.3&amp;REQUEST=GetLegendGraphic&amp;LAYER=testlayer èé&amp;FORMAT=image/png&amp;STYLE=default"/>
</LegendURL>
</Style>
</Layer>
</Layer>
</Capability>
</WMS_Capabilities>
152 changes: 152 additions & 0 deletions tests/testdata/qgis_server/getprojectsettings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
Content-Type: text/xml; charset=utf-8
Content-Length: 6231

<?xml version="1.0" encoding="utf-8"?>
<WMS_Capabilities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3.0" xmlns="http://www.opengis.net/wms" xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd http://www.qgis.org/wms http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;SERVICE=WMS&amp;REQUEST=GetSchemaExtension" xmlns:sld="http://www.opengis.net/sld" xmlns:qgs="http://www.qgis.org/wms">
<Service>
<Name>WMS</Name>
<Title>QGIS TestProject</Title>
<Abstract>Some UTF8 text èòù</Abstract>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href=""/>
<ContactInformation>
<ContactPersonPrimary>
<ContactPerson>Alessandro Pasotti</ContactPerson>
<ContactOrganization>QGIS dev team</ContactOrganization>
</ContactPersonPrimary>
<ContactVoiceTelephone></ContactVoiceTelephone>
<ContactElectronicMailAddress>elpaso@itopen.it</ContactElectronicMailAddress>
</ContactInformation>
<Fees></Fees>
<AccessConstraints></AccessConstraints>
</Service>
<Capability>
<Request>
<GetCapabilities>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetCapabilities>
<GetMap>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<Format>image/png; mode=16bit</Format>
<Format>image/png; mode=8bit</Format>
<Format>image/png; mode=1bit</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetMap>
<GetFeatureInfo>
<Format>text/plain</Format>
<Format>text/html</Format>
<Format>text/xml</Format>
<Format>application/vnd.ogc.gml</Format>
<Format>application/vnd.ogc.gml/3.1.1</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetFeatureInfo>
<sld:GetLegendGraphic>
<Format>image/jpeg</Format>
<Format>image/png</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</sld:GetLegendGraphic>
<sld:DescribeLayer>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</sld:DescribeLayer>
<qgs:GetStyles>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</qgs:GetStyles>
<GetPrint>
<Format>svg</Format>
<Format>png</Format>
<Format>pdf</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetPrint>
</Request>
<Exception>
<Format>text/xml</Format>
</Exception>
<sld:UserDefinedSymbolization RemoteWFS="0" RemoteWCS="0" InlineFeature="0" UserStyle="1" SupportSLD="1" UserLayer="0"/>
<Layer queryable="1">
<Name>QGIS Test Project</Name>
<Title>QGIS Test Project</Title>
<CRS>EPSG:4326</CRS>
<CRS>EPSG:3857</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>8.20315</westBoundLongitude>
<eastBoundLongitude>8.20416</eastBoundLongitude>
<southBoundLatitude>44.9012</southBoundLatitude>
<northBoundLatitude>44.9016</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="EPSG:4326" maxx="44.9016" minx="44.9012" maxy="8.20416" miny="8.20315"/>
<Layer displayField="name" geometryType="WKBPoint" queryable="1" visible="1">
<Name>testlayer èé</Name>
<Title>A test vector layer</Title>
<Abstract>A test vector layer with unicode òà</Abstract>
<CRS>EPSG:4326</CRS>
<CRS>EPSG:3857</CRS>
<EX_GeographicBoundingBox>
<westBoundLongitude>8.20346</westBoundLongitude>
<eastBoundLongitude>8.20355</eastBoundLongitude>
<southBoundLatitude>44.9014</southBoundLatitude>
<northBoundLatitude>44.9015</northBoundLatitude>
</EX_GeographicBoundingBox>
<BoundingBox CRS="EPSG:4326" maxx="44.9015" minx="44.9014" maxy="8.20355" miny="8.20346"/>
<Style>
<Name>default</Name>
<Title>default</Title>
<LegendURL>
<Format>image/png</Format>
<OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http:?MAP=/home/ale/dev/QGIS/tests/testdata/qgis_server/testproject.qgs&amp;&amp;SERVICE=WMS&amp;VERSION=1.3.0&amp;REQUEST=GetLegendGraphic&amp;LAYER=testlayer èé&amp;FORMAT=image/png&amp;STYLE=default&amp;SLD_VERSION=1.1.0"/>
</LegendURL>
</Style>
<Attributes>
<Attribute typeName="Integer" precision="0" length="10" editType="TextEdit" type="int" comment="" name="id"/>
<Attribute typeName="String" precision="0" length="10" editType="TextEdit" type="QString" comment="" name="name"/>
<Attribute typeName="String" precision="0" length="13" editType="TextEdit" type="QString" comment="" name="utf8nameè"/>
</Attributes>
</Layer>
</Layer>
<LayerDrawingOrder>testlayer èé</LayerDrawingOrder>
</Capability>
</WMS_Capabilities>
Binary file added tests/testdata/qgis_server/testlayer.dbf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/testlayer.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
1 change: 1 addition & 0 deletions tests/testdata/qgis_server/testlayer.qpj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
Binary file added tests/testdata/qgis_server/testlayer.shp
Binary file not shown.
Binary file added tests/testdata/qgis_server/testlayer.shx
Binary file not shown.
418 changes: 418 additions & 0 deletions tests/testdata/qgis_server/testproject.qgs

Large diffs are not rendered by default.