109 changes: 105 additions & 4 deletions src/mapserver/qgis_map_serv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ map service syntax for SOAP/HTTP POST
#include "qgsproviderregistry.h"
#include "qgslogger.h"
#include "qgswmsserver.h"
#include "qgswfsserver.h"
#include "qgsmaprenderer.h"
#include "qgsmapserviceexception.h"
#include "qgsprojectparser.h"
Expand Down Expand Up @@ -264,24 +265,124 @@ int main( int argc, char * argv[] )

//request to WMS?
QString serviceString;
#ifndef QGISDEBUG
serviceString = parameterMap.value( "SERVICE", "WMS" );
#else
paramIt = parameterMap.find( "SERVICE" );
if ( paramIt == parameterMap.constEnd() )
{
#ifndef QGISDEBUG
serviceString = parameterMap.value( "SERVICE", "WMS" );
#else
QgsDebugMsg( "unable to find 'SERVICE' parameter, exiting..." );
theRequestHandler->sendServiceException( QgsMapServiceException( "ServiceNotSpecified", "Service not specified. The SERVICE parameter is mandatory" ) );
delete theRequestHandler;
continue;
#endif
}
else
{
serviceString = paramIt.value();
}
#endif

QgsWMSServer* theServer = 0;
if ( serviceString == "WFS" )
{
delete theServer;
QgsWFSServer* theServer = 0;
try
{
theServer = new QgsWFSServer( parameterMap );
}
catch ( QgsMapServiceException e ) //admin.sld may be invalid
{
theRequestHandler->sendServiceException( e );
continue;
}

theServer->setAdminConfigParser( adminConfigParser );


//request type
QString request = parameterMap.value( "REQUEST" );
if ( request.isEmpty() )
{
//do some error handling
QgsDebugMsg( "unable to find 'REQUEST' parameter, exiting..." );
theRequestHandler->sendServiceException( QgsMapServiceException( "OperationNotSupported", "Please check the value of the REQUEST parameter" ) );
delete theRequestHandler;
delete theServer;
continue;
}

if ( request == "GetCapabilities" )
{
QDomDocument capabilitiesDocument;
try
{
capabilitiesDocument = theServer->getCapabilities();
}
catch ( QgsMapServiceException& ex )
{
theRequestHandler->sendServiceException( ex );
delete theRequestHandler;
delete theServer;
continue;
}
QgsDebugMsg( "sending GetCapabilities response" );
theRequestHandler->sendGetCapabilitiesResponse( capabilitiesDocument );
delete theRequestHandler;
delete theServer;
continue;
}
else if ( request == "DescribeFeatureType" )
{
QDomDocument describeDocument;
try
{
describeDocument = theServer->describeFeatureType();
}
catch ( QgsMapServiceException& ex )
{
theRequestHandler->sendServiceException( ex );
delete theRequestHandler;
delete theServer;
continue;
}
QgsDebugMsg( "sending GetCapabilities response" );
theRequestHandler->sendGetCapabilitiesResponse( describeDocument );
delete theRequestHandler;
delete theServer;
continue;
}
else if ( request == "GetFeature" )
{
//output format for GetFeature
QString outputFormat = parameterMap.value( "OUTPUTFORMAT" );
try
{
if ( theServer->getFeature( *theRequestHandler, outputFormat ) != 0 )
{
delete theRequestHandler;
delete theServer;
continue;
}
else
{
delete theRequestHandler;
delete theServer;
continue;
}
}
catch ( QgsMapServiceException& ex )
{
theRequestHandler->sendServiceException( ex );
delete theRequestHandler;
delete theServer;
continue;
}
}

return 0;
}

try
{
theServer = new QgsWMSServer( parameterMap, theMapRenderer );
Expand Down
4 changes: 4 additions & 0 deletions src/mapserver/qgsconfigparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class QgsConfigParser
/**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.*/
virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;

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

/**Returns one or possibly several maplayers for a given layer name and style. If there are several layers, the layers should be drawn in inverse list order.
If no layers/style are found, an empty list is returned
@param allowCache true if layer can be read from / written to cache*/
Expand Down Expand Up @@ -87,6 +89,8 @@ class QgsConfigParser

/**Returns an ID-list of layers which are not queryable*/
virtual QStringList identifyDisabledLayers() const { return QStringList(); }
/**Returns an ID-list of layers which queryable in WFS service*/
virtual QStringList wfsLayers() const { return QStringList(); }

/**Returns a set of supported epsg codes for the capabilities document. An empty list means
that all possible CRS should be advertised (which could result in very long capabilities documents)*/
Expand Down
50 changes: 50 additions & 0 deletions src/mapserver/qgshttprequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,56 @@ void QgsHttpRequestHandler::sendGetPrintResponse( QByteArray* ba ) const
sendHttpResponse( ba, formatToMimeType( mFormat ) );
}

bool QgsHttpRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const
{
if ( !ba )
{
return false;
}

if ( ba->size() < 1 )
{
return false;
}

QString format;
if ( infoFormat == "GeoJSON" )
format = "text/plain";
else
format = "text/xml";

printf( "Content-Type: " );
printf( format.toLocal8Bit() );
printf( "\n" );
printf( "\n" );
fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
return true;
}

void QgsHttpRequestHandler::sendGetFeatureResponse( QByteArray* ba ) const
{
if ( !ba )
{
return;
}

if ( ba->size() < 1 )
{
return;
}
fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
}

void QgsHttpRequestHandler::endGetFeatureResponse( QByteArray* ba ) const
{
if ( !ba )
{
return;
}

fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
}

void QgsHttpRequestHandler::requestStringToParameterMap( const QString& request, QMap<QString, QString>& parameters )
{
parameters.clear();
Expand Down
3 changes: 3 additions & 0 deletions src/mapserver/qgshttprequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class QgsHttpRequestHandler: public QgsRequestHandler
virtual void sendServiceException( const QgsMapServiceException& ex ) const;
virtual void sendGetStyleResponse( const QDomDocument& doc ) const;
virtual void sendGetPrintResponse( QByteArray* ba ) const;
virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const;
virtual void sendGetFeatureResponse( QByteArray* ba ) const;
virtual void endGetFeatureResponse( QByteArray* ba ) const;

protected:
void sendHttpResponse( QByteArray* ba, const QString& format ) const;
Expand Down
89 changes: 89 additions & 0 deletions src/mapserver/qgsprojectparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,64 @@ void QgsProjectParser::layersAndStylesCapabilities( QDomElement& parentElement,
combineExtentAndCrsOfGroupChildren( layerParentElem, doc );
}

void QgsProjectParser::featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const
{
QStringList wfsLayersId = wfsLayers();

if ( mProjectLayerElements.size() < 1 )
{
return;
}

QMap<QString, QgsMapLayer *> layerMap;

foreach( const QDomElement &elem, mProjectLayerElements )
{
QString type = elem.attribute( "type" );
if ( type == "vector" )
{
//QgsMapLayer *layer = createLayerFromElement( *layerIt );
QgsMapLayer *layer = createLayerFromElement( elem );
if ( layer && wfsLayersId.contains( layer->id() ) )
{
QgsDebugMsg( QString( "add layer %1 to map" ).arg( layer->id() ) );
layerMap.insert( layer->id(), layer );

QDomElement layerElem = doc.createElement( "FeatureType" );
QDomElement nameElem = doc.createElement( "Name" );
//We use the layer name even though it might not be unique.
//Because the id sometimes contains user/pw information and the name is more descriptive
QDomText nameText = doc.createTextNode( layer->name() );
nameElem.appendChild( nameText );
layerElem.appendChild( nameElem );

QDomElement titleElem = doc.createElement( "Title" );
QDomText titleText = doc.createTextNode( layer->name() );
titleElem.appendChild( titleText );
layerElem.appendChild( titleElem );

//appendExGeographicBoundingBox( layerElem, doc, layer->extent(), layer->crs() );

QDomElement srsElem = doc.createElement( "SRS" );
QDomText srsText = doc.createTextNode( layer->crs().authid() );
srsElem.appendChild( srsText );
layerElem.appendChild( srsElem );

QgsRectangle layerExtent = layer->extent();
QDomElement bBoxElement = doc.createElement( "LatLongBoundingBox" );
bBoxElement.setAttribute( "minx", QString::number( layerExtent.xMinimum() ) );
bBoxElement.setAttribute( "miny", QString::number( layerExtent.yMinimum() ) );
bBoxElement.setAttribute( "maxx", QString::number( layerExtent.xMaximum() ) );
bBoxElement.setAttribute( "maxy", QString::number( layerExtent.yMaximum() ) );
layerElem.appendChild( bBoxElement );

parentElement.appendChild( layerElem );
}
}
}
return;
}

void QgsProjectParser::addLayers( QDomDocument &doc,
QDomElement &parentElem,
const QDomElement &legendElem,
Expand Down Expand Up @@ -584,6 +642,37 @@ QStringList QgsProjectParser::identifyDisabledLayers() const
return disabledList;
}

QStringList QgsProjectParser::wfsLayers() const
{
QStringList wfsList;
if ( !mXMLDoc )
{
return wfsList;
}

QDomElement qgisElem = mXMLDoc->documentElement();
if ( qgisElem.isNull() )
{
return wfsList;
}
QDomElement propertiesElem = qgisElem.firstChildElement( "properties" );
if ( propertiesElem.isNull() )
{
return wfsList;
}
QDomElement wfsLayersElem = propertiesElem.firstChildElement( "WFSLayers" );
if ( wfsLayersElem.isNull() )
{
return wfsList;
}
QDomNodeList valueList = wfsLayersElem.elementsByTagName( "value" );
for ( int i = 0; i < valueList.size(); ++i )
{
wfsList << valueList.at( i ).toElement().text();
}
return wfsList;
}

QStringList QgsProjectParser::supportedOutputCrsList() const
{
QStringList crsList;
Expand Down
5 changes: 5 additions & 0 deletions src/mapserver/qgsprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class QgsProjectParser: public QgsConfigParser
/**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.*/
virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;

virtual void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const;

int numberOfLayers() const;

/**Returns one or possibly several maplayers for a given layer name and style. If no layers/style are found, an empty list is returned*/
Expand All @@ -58,6 +60,9 @@ class QgsProjectParser: public QgsConfigParser
/**Returns an ID-list of layers which are not queryable (comes from <properties> -> <Identify> -> <disabledLayers in the project file*/
virtual QStringList identifyDisabledLayers() const;

/**Returns an ID-list of layers queryable for WFS service (comes from <properties> -> <WFSLayers> in the project file*/
virtual QStringList wfsLayers() const;

/**Returns a set of supported epsg codes for the capabilities document. The list comes from the property <WMSEpsgList> in the project file.
An empty set means that all possible CRS should be advertised (which could result in very long capabilities documents)
Example:
Expand Down
3 changes: 3 additions & 0 deletions src/mapserver/qgsrequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class QgsRequestHandler
virtual void sendServiceException( const QgsMapServiceException& ex ) const = 0;
virtual void sendGetStyleResponse( const QDomDocument& doc ) const = 0;
virtual void sendGetPrintResponse( QByteArray* ba ) const = 0;
virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const = 0;
virtual void sendGetFeatureResponse( QByteArray* ba ) const = 0;
virtual void endGetFeatureResponse( QByteArray* ba ) const = 0;
QString format() const { return mFormat; }
protected:
/**This is set by the parseInput methods of the subclasses (parameter FORMAT, e.g. 'FORMAT=PNG')*/
Expand Down
2 changes: 2 additions & 0 deletions src/mapserver/qgssldparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class QgsSLDParser: public QgsConfigParser
/**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.*/
void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;

void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const {};

/**Returns number of layers in configuration*/
int numberOfLayers() const;

Expand Down
946 changes: 946 additions & 0 deletions src/mapserver/qgswfsserver.cpp

Large diffs are not rendered by default.

92 changes: 92 additions & 0 deletions src/mapserver/qgswfsserver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

#ifndef QGSWFSSERVER_H
#define QGSWFSSERVER_H

#include <QDomDocument>
#include <QMap>
#include <QString>
#include <map>

class QgsCoordinateReferenceSystem;
class QgsComposerLayerItem;
class QgsComposerLegendItem;
class QgsComposition;
class QgsMapLayer;
class QgsMapRenderer;
class QgsPoint;
class QgsRasterLayer;
class QgsConfigParser;
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
class QgsField;
class QgsFeature;
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:
/**Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWFSServer( QMap<QString, QString> parameters );
~QgsWFSServer();
/**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 );

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

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

/**Map containing the WMS parameters*/
QMap<QString, QString> mParameterMap;
QgsConfigParser* mConfigParser;
QString mTypeName;
bool mWithGeom;

protected:

void startGetFeature( QgsRequestHandler& request, const QString& format );
void sendGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes );
void endGetFeature( QgsRequestHandler& request, const QString& format );

//methods to write GeoJSON
QString createFeatureGeoJSON( QgsFeature* feat, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;

//methods to write GML2
QDomElement createFeatureElem( QgsFeature* feat, QDomDocument& doc, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;

QDomElement createGeometryElem( QgsGeometry* g, QDomDocument& doc ) /*const*/;
QDomElement createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;

/**Create a GML coordinate string from a point list.
@param points list of data points
@param coordString out: GML coord string
@return 0 in case of success*/
QDomElement createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const;
};

#endif
38 changes: 35 additions & 3 deletions src/ui/qgsprojectpropertiesbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,9 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<widget class="QWidget" name="tab4">
<attribute name="title">
<string>WMS Server</string>
<string>OWS Server</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" rowspan="2">
Expand All @@ -384,7 +384,7 @@
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="grpWMSServiceCapabilities">
<widget class="QGroupBox" name="grpOWSServiceCapabilities">
<property name="title">
<string>Service Capabilitities</string>
</property>
Expand Down Expand Up @@ -483,6 +483,12 @@
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="grpWMSCapabilities">
<property name="title">
<string>WMS Capabilitities</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="1" column="0">
<widget class="QGroupBox" name="grpWMSExt">
<property name="title">
Expand Down Expand Up @@ -631,6 +637,32 @@
<string>Add WKT geometry to feature info response</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QGroupBox" name="grpWFSCapabilities">
<property name="title">
<string>WFS Capabilitities</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QTableWidget" name="twWFSLayers">
<column>
<property name="text">
<string>Layer</string>
</property>
</column>
<column>
<property name="text">
<string>Published</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
Expand Down