Skip to content

Commit c27c890

Browse files
committedMar 20, 2012
[FEATURE]: Add WFS support for QGIS server. Provided by René-Luc D'Hont
1 parent a2ee769 commit c27c890

15 files changed

+1676
-9
lines changed
 

‎src/app/qgsprojectproperties.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
175175
twIdentifyLayers->setCellWidget( i, 2, cb );
176176
}
177177

178-
grpWMSServiceCapabilities->setChecked( QgsProject::instance()->readBoolEntry( "WMSServiceCapabilities", "/", false ) );
178+
grpOWSServiceCapabilities->setChecked( QgsProject::instance()->readBoolEntry( "WMSServiceCapabilities", "/", false ) );
179179
mWMSTitle->setText( QgsProject::instance()->readEntry( "WMSServiceTitle", "/" ) );
180180
mWMSContactOrganization->setText( QgsProject::instance()->readEntry( "WMSContactOrganization", "/", "" ) );
181181
mWMSContactPerson->setText( QgsProject::instance()->readEntry( "WMSContactPerson", "/", "" ) );
@@ -229,6 +229,38 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
229229
bool addWktGeometry = QgsProject::instance()->readBoolEntry( "WMSAddWktGeometry", "/" );
230230
mAddWktGeometryCheckBox->setChecked( addWktGeometry );
231231

232+
QStringList wfsLayerIdList = QgsProject::instance()->readListEntry( "WFSLayers", "/" );
233+
234+
twWFSLayers->setColumnCount( 2 );
235+
twWFSLayers->horizontalHeader()->setVisible( true );
236+
twWFSLayers->setRowCount( mapLayers.size() );
237+
238+
i = 0;
239+
int j = 0;
240+
for ( QMap<QString, QgsMapLayer*>::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); it++, i++ )
241+
{
242+
currentLayer = it.value();
243+
if ( currentLayer->type() == QgsMapLayer::VectorLayer )
244+
{
245+
246+
QTableWidgetItem *twi = new QTableWidgetItem( QString::number( j ) );
247+
twWFSLayers->setVerticalHeaderItem( j, twi );
248+
249+
twi = new QTableWidgetItem( currentLayer->name() );
250+
twi->setData( Qt::UserRole, it.key() );
251+
twi->setFlags( twi->flags() & ~Qt::ItemIsEditable );
252+
twWFSLayers->setItem( j, 0, twi );
253+
254+
QCheckBox *cb = new QCheckBox();
255+
cb->setChecked( wfsLayerIdList.contains( currentLayer->id() ) );
256+
twWFSLayers->setCellWidget( j, 1, cb );
257+
j++;
258+
259+
}
260+
}
261+
twWFSLayers->setRowCount( j );
262+
twWFSLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents );
263+
232264
restoreState();
233265
}
234266

@@ -380,7 +412,7 @@ void QgsProjectProperties::apply()
380412

381413
QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", noIdentifyLayerList );
382414

383-
QgsProject::instance()->writeEntry( "WMSServiceCapabilities", "/", grpWMSServiceCapabilities->isChecked() );
415+
QgsProject::instance()->writeEntry( "WMSServiceCapabilities", "/", grpOWSServiceCapabilities->isChecked() );
384416
QgsProject::instance()->writeEntry( "WMSServiceTitle", "/", mWMSTitle->text() );
385417
QgsProject::instance()->writeEntry( "WMSContactOrganization", "/", mWMSContactOrganization->text() );
386418
QgsProject::instance()->writeEntry( "WMSContactPerson", "/", mWMSContactPerson->text() );
@@ -428,6 +460,18 @@ void QgsProjectProperties::apply()
428460

429461
QgsProject::instance()->writeEntry( "WMSAddWktGeometry", "/", mAddWktGeometryCheckBox->isChecked() );
430462

463+
QStringList wfsLayerList;
464+
for ( int i = 0; i < twWFSLayers->rowCount(); i++ )
465+
{
466+
QCheckBox *cb = qobject_cast<QCheckBox *>( twWFSLayers->cellWidget( i, 1 ) );
467+
if ( cb && cb->isChecked() )
468+
{
469+
QString id = twWFSLayers->item( i, 0 )->data( Qt::UserRole ).toString();
470+
wfsLayerList << id;
471+
}
472+
}
473+
QgsProject::instance()->writeEntry( "WFSLayers", "/", wfsLayerList );
474+
431475
//todo XXX set canvas color
432476
emit refresh();
433477
}

‎src/core/qgsgeometry.cpp

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4185,6 +4185,296 @@ QString QgsGeometry::exportToWkt()
41854185
}
41864186
}
41874187

4188+
QString QgsGeometry::exportToGeoJSON()
4189+
{
4190+
QgsDebugMsg( "entered." );
4191+
4192+
// TODO: implement with GEOS
4193+
if ( mDirtyWkb )
4194+
{
4195+
exportGeosToWkb();
4196+
}
4197+
4198+
if ( !mGeometry )
4199+
{
4200+
QgsDebugMsg( "WKB geometry not available!" );
4201+
return QString::null;
4202+
}
4203+
4204+
QGis::WkbType wkbType;
4205+
bool hasZValue = false;
4206+
double *x, *y;
4207+
4208+
QString mWkt; // TODO: rename
4209+
4210+
// Will this really work when mGeometry[0] == 0 ???? I (gavin) think not.
4211+
//wkbType = (mGeometry[0] == 1) ? mGeometry[1] : mGeometry[4];
4212+
memcpy( &wkbType, &( mGeometry[1] ), sizeof( int ) );
4213+
4214+
switch ( wkbType )
4215+
{
4216+
case QGis::WKBPoint25D:
4217+
case QGis::WKBPoint:
4218+
{
4219+
mWkt += "{ \"type\": \"Point\", \"coordinates\": [";
4220+
x = ( double * )( mGeometry + 5 );
4221+
mWkt += QString::number( *x, 'f', 6 );
4222+
mWkt += ", ";
4223+
y = ( double * )( mGeometry + 5 + sizeof( double ) );
4224+
mWkt += QString::number( *y, 'f', 6 );
4225+
mWkt += "] }";
4226+
return mWkt;
4227+
}
4228+
4229+
case QGis::WKBLineString25D:
4230+
hasZValue = true;
4231+
case QGis::WKBLineString:
4232+
{
4233+
QgsDebugMsg( "LINESTRING found" );
4234+
unsigned char *ptr;
4235+
int *nPoints;
4236+
int idx;
4237+
4238+
mWkt += "{ \"type\": \"LineString\", \"coordinates\": [ ";
4239+
// get number of points in the line
4240+
ptr = mGeometry + 5;
4241+
nPoints = ( int * ) ptr;
4242+
ptr = mGeometry + 1 + 2 * sizeof( int );
4243+
for ( idx = 0; idx < *nPoints; ++idx )
4244+
{
4245+
if ( idx != 0 )
4246+
{
4247+
mWkt += ", ";
4248+
}
4249+
mWkt += "[";
4250+
x = ( double * ) ptr;
4251+
mWkt += QString::number( *x, 'f', 6 );
4252+
mWkt += ", ";
4253+
ptr += sizeof( double );
4254+
y = ( double * ) ptr;
4255+
mWkt += QString::number( *y, 'f', 6 );
4256+
ptr += sizeof( double );
4257+
if ( hasZValue )
4258+
{
4259+
ptr += sizeof( double );
4260+
}
4261+
mWkt += "]";
4262+
}
4263+
mWkt += " ] }";
4264+
return mWkt;
4265+
}
4266+
4267+
case QGis::WKBPolygon25D:
4268+
hasZValue = true;
4269+
case QGis::WKBPolygon:
4270+
{
4271+
QgsDebugMsg( "POLYGON found" );
4272+
unsigned char *ptr;
4273+
int idx, jdx;
4274+
int *numRings, *nPoints;
4275+
4276+
mWkt += "{ \"type\": \"Polygon\", \"coordinates\": [ ";
4277+
// get number of rings in the polygon
4278+
numRings = ( int * )( mGeometry + 1 + sizeof( int ) );
4279+
if ( !( *numRings ) ) // sanity check for zero rings in polygon
4280+
{
4281+
return QString();
4282+
}
4283+
int *ringStart; // index of first point for each ring
4284+
int *ringNumPoints; // number of points in each ring
4285+
ringStart = new int[*numRings];
4286+
ringNumPoints = new int[*numRings];
4287+
ptr = mGeometry + 1 + 2 * sizeof( int ); // set pointer to the first ring
4288+
for ( idx = 0; idx < *numRings; idx++ )
4289+
{
4290+
if ( idx != 0 )
4291+
{
4292+
mWkt += ", ";
4293+
}
4294+
mWkt += "[ ";
4295+
// get number of points in the ring
4296+
nPoints = ( int * ) ptr;
4297+
ringNumPoints[idx] = *nPoints;
4298+
ptr += 4;
4299+
4300+
for ( jdx = 0; jdx < *nPoints; jdx++ )
4301+
{
4302+
if ( jdx != 0 )
4303+
{
4304+
mWkt += ", ";
4305+
}
4306+
mWkt += "[";
4307+
x = ( double * ) ptr;
4308+
mWkt += QString::number( *x, 'f', 6 );
4309+
mWkt += ", ";
4310+
ptr += sizeof( double );
4311+
y = ( double * ) ptr;
4312+
mWkt += QString::number( *y, 'f', 6 );
4313+
ptr += sizeof( double );
4314+
if ( hasZValue )
4315+
{
4316+
ptr += sizeof( double );
4317+
}
4318+
mWkt += "]";
4319+
}
4320+
mWkt += " ]";
4321+
}
4322+
mWkt += " ] }";
4323+
delete [] ringStart;
4324+
delete [] ringNumPoints;
4325+
return mWkt;
4326+
}
4327+
4328+
case QGis::WKBMultiPoint25D:
4329+
hasZValue = true;
4330+
case QGis::WKBMultiPoint:
4331+
{
4332+
unsigned char *ptr;
4333+
int idx;
4334+
int *nPoints;
4335+
4336+
mWkt += "{ \"type\": \"MultiPoint\", \"coordinates\": [ ";
4337+
nPoints = ( int* )( mGeometry + 5 );
4338+
ptr = mGeometry + 5 + sizeof( int );
4339+
for ( idx = 0; idx < *nPoints; ++idx )
4340+
{
4341+
ptr += ( 1 + sizeof( int ) );
4342+
if ( idx != 0 )
4343+
{
4344+
mWkt += ", ";
4345+
}
4346+
mWkt += "[";
4347+
x = ( double * )( ptr );
4348+
mWkt += QString::number( *x, 'f', 6 );
4349+
mWkt += ", ";
4350+
ptr += sizeof( double );
4351+
y = ( double * )( ptr );
4352+
mWkt += QString::number( *y, 'f', 6 );
4353+
ptr += sizeof( double );
4354+
if ( hasZValue )
4355+
{
4356+
ptr += sizeof( double );
4357+
}
4358+
mWkt += "]";
4359+
}
4360+
mWkt += " ] }";
4361+
return mWkt;
4362+
}
4363+
4364+
case QGis::WKBMultiLineString25D:
4365+
hasZValue = true;
4366+
case QGis::WKBMultiLineString:
4367+
{
4368+
QgsDebugMsg( "MULTILINESTRING found" );
4369+
unsigned char *ptr;
4370+
int idx, jdx, numLineStrings;
4371+
int *nPoints;
4372+
4373+
mWkt += "{ \"type\": \"MultiLineString\", \"coordinates\": [ ";
4374+
numLineStrings = ( int )( mGeometry[5] );
4375+
ptr = mGeometry + 9;
4376+
for ( jdx = 0; jdx < numLineStrings; jdx++ )
4377+
{
4378+
if ( jdx != 0 )
4379+
{
4380+
mWkt += ", ";
4381+
}
4382+
mWkt += "[ ";
4383+
ptr += 5; // skip type since we know its 2
4384+
nPoints = ( int * ) ptr;
4385+
ptr += sizeof( int );
4386+
for ( idx = 0; idx < *nPoints; idx++ )
4387+
{
4388+
if ( idx != 0 )
4389+
{
4390+
mWkt += ", ";
4391+
}
4392+
mWkt += "[";
4393+
x = ( double * ) ptr;
4394+
mWkt += QString::number( *x, 'f', 6 );
4395+
ptr += sizeof( double );
4396+
mWkt += ", ";
4397+
y = ( double * ) ptr;
4398+
mWkt += QString::number( *y, 'f', 6 );
4399+
ptr += sizeof( double );
4400+
if ( hasZValue )
4401+
{
4402+
ptr += sizeof( double );
4403+
}
4404+
mWkt += "]";
4405+
}
4406+
mWkt += " ]";
4407+
}
4408+
mWkt += " ] }";
4409+
return mWkt;
4410+
}
4411+
4412+
case QGis::WKBMultiPolygon25D:
4413+
hasZValue = true;
4414+
case QGis::WKBMultiPolygon:
4415+
{
4416+
QgsDebugMsg( "MULTIPOLYGON found" );
4417+
unsigned char *ptr;
4418+
int idx, jdx, kdx;
4419+
int *numPolygons, *numRings, *nPoints;
4420+
4421+
mWkt += "{ \"type\": \"MultiPolygon\", \"coordinates\": [ ";
4422+
ptr = mGeometry + 5;
4423+
numPolygons = ( int * ) ptr;
4424+
ptr = mGeometry + 9;
4425+
for ( kdx = 0; kdx < *numPolygons; kdx++ )
4426+
{
4427+
if ( kdx != 0 )
4428+
{
4429+
mWkt += ", ";
4430+
}
4431+
mWkt += "[ ";
4432+
ptr += 5;
4433+
numRings = ( int * ) ptr;
4434+
ptr += 4;
4435+
for ( idx = 0; idx < *numRings; idx++ )
4436+
{
4437+
if ( idx != 0 )
4438+
{
4439+
mWkt += ", ";
4440+
}
4441+
mWkt += "[ ";
4442+
nPoints = ( int * ) ptr;
4443+
ptr += 4;
4444+
for ( jdx = 0; jdx < *nPoints; jdx++ )
4445+
{
4446+
if ( jdx != 0 )
4447+
{
4448+
mWkt += ", ";
4449+
}
4450+
mWkt += "[";
4451+
x = ( double * ) ptr;
4452+
mWkt += QString::number( *x, 'f', 6 );
4453+
ptr += sizeof( double );
4454+
mWkt += ", ";
4455+
y = ( double * ) ptr;
4456+
mWkt += QString::number( *y, 'f', 6 );
4457+
ptr += sizeof( double );
4458+
if ( hasZValue )
4459+
{
4460+
ptr += sizeof( double );
4461+
}
4462+
mWkt += "]";
4463+
}
4464+
mWkt += " ]";
4465+
}
4466+
mWkt += " ]";
4467+
}
4468+
mWkt += " ] }";
4469+
return mWkt;
4470+
}
4471+
4472+
default:
4473+
QgsDebugMsg( "error: mGeometry type not recognized" );
4474+
return QString::null;
4475+
}
4476+
}
4477+
41884478
bool QgsGeometry::exportWkbToGeos()
41894479
{
41904480
QgsDebugMsgLevel( "entered.", 3 );

‎src/core/qgsgeometry.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,11 @@ class CORE_EXPORT QgsGeometry
361361
*/
362362
QString exportToWkt();
363363

364+
/** Exports the geometry to mGeoJSON
365+
@return true in case of success and false else
366+
*/
367+
QString exportToGeoJSON();
368+
364369
/* Accessor functions for getting geometry data */
365370

366371
/** return contents of the geometry as a point

‎src/mapserver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ SET ( qgis_mapserv_SRCS
2828
qgssldparser.cpp
2929
qgssldrenderer.cpp
3030
qgswmsserver.cpp
31+
qgswfsserver.cpp
3132
qgsmapserviceexception.cpp
3233
qgsmslayercache.cpp
3334
qgsfilter.cpp

‎src/mapserver/qgis_map_serv.cpp

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ map service syntax for SOAP/HTTP POST
2626
#include "qgsproviderregistry.h"
2727
#include "qgslogger.h"
2828
#include "qgswmsserver.h"
29+
#include "qgswfsserver.h"
2930
#include "qgsmaprenderer.h"
3031
#include "qgsmapserviceexception.h"
3132
#include "qgsprojectparser.h"
@@ -264,24 +265,124 @@ int main( int argc, char * argv[] )
264265

265266
//request to WMS?
266267
QString serviceString;
267-
#ifndef QGISDEBUG
268-
serviceString = parameterMap.value( "SERVICE", "WMS" );
269-
#else
270268
paramIt = parameterMap.find( "SERVICE" );
271269
if ( paramIt == parameterMap.constEnd() )
272270
{
271+
#ifndef QGISDEBUG
272+
serviceString = parameterMap.value( "SERVICE", "WMS" );
273+
#else
273274
QgsDebugMsg( "unable to find 'SERVICE' parameter, exiting..." );
274275
theRequestHandler->sendServiceException( QgsMapServiceException( "ServiceNotSpecified", "Service not specified. The SERVICE parameter is mandatory" ) );
275276
delete theRequestHandler;
276277
continue;
278+
#endif
277279
}
278280
else
279281
{
280282
serviceString = paramIt.value();
281283
}
282-
#endif
283284

284285
QgsWMSServer* theServer = 0;
286+
if ( serviceString == "WFS" )
287+
{
288+
delete theServer;
289+
QgsWFSServer* theServer = 0;
290+
try
291+
{
292+
theServer = new QgsWFSServer( parameterMap );
293+
}
294+
catch ( QgsMapServiceException e ) //admin.sld may be invalid
295+
{
296+
theRequestHandler->sendServiceException( e );
297+
continue;
298+
}
299+
300+
theServer->setAdminConfigParser( adminConfigParser );
301+
302+
303+
//request type
304+
QString request = parameterMap.value( "REQUEST" );
305+
if ( request.isEmpty() )
306+
{
307+
//do some error handling
308+
QgsDebugMsg( "unable to find 'REQUEST' parameter, exiting..." );
309+
theRequestHandler->sendServiceException( QgsMapServiceException( "OperationNotSupported", "Please check the value of the REQUEST parameter" ) );
310+
delete theRequestHandler;
311+
delete theServer;
312+
continue;
313+
}
314+
315+
if ( request == "GetCapabilities" )
316+
{
317+
QDomDocument capabilitiesDocument;
318+
try
319+
{
320+
capabilitiesDocument = theServer->getCapabilities();
321+
}
322+
catch ( QgsMapServiceException& ex )
323+
{
324+
theRequestHandler->sendServiceException( ex );
325+
delete theRequestHandler;
326+
delete theServer;
327+
continue;
328+
}
329+
QgsDebugMsg( "sending GetCapabilities response" );
330+
theRequestHandler->sendGetCapabilitiesResponse( capabilitiesDocument );
331+
delete theRequestHandler;
332+
delete theServer;
333+
continue;
334+
}
335+
else if ( request == "DescribeFeatureType" )
336+
{
337+
QDomDocument describeDocument;
338+
try
339+
{
340+
describeDocument = theServer->describeFeatureType();
341+
}
342+
catch ( QgsMapServiceException& ex )
343+
{
344+
theRequestHandler->sendServiceException( ex );
345+
delete theRequestHandler;
346+
delete theServer;
347+
continue;
348+
}
349+
QgsDebugMsg( "sending GetCapabilities response" );
350+
theRequestHandler->sendGetCapabilitiesResponse( describeDocument );
351+
delete theRequestHandler;
352+
delete theServer;
353+
continue;
354+
}
355+
else if ( request == "GetFeature" )
356+
{
357+
//output format for GetFeature
358+
QString outputFormat = parameterMap.value( "OUTPUTFORMAT" );
359+
try
360+
{
361+
if ( theServer->getFeature( *theRequestHandler, outputFormat ) != 0 )
362+
{
363+
delete theRequestHandler;
364+
delete theServer;
365+
continue;
366+
}
367+
else
368+
{
369+
delete theRequestHandler;
370+
delete theServer;
371+
continue;
372+
}
373+
}
374+
catch ( QgsMapServiceException& ex )
375+
{
376+
theRequestHandler->sendServiceException( ex );
377+
delete theRequestHandler;
378+
delete theServer;
379+
continue;
380+
}
381+
}
382+
383+
return 0;
384+
}
385+
285386
try
286387
{
287388
theServer = new QgsWMSServer( parameterMap, theMapRenderer );

‎src/mapserver/qgsconfigparser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class QgsConfigParser
4242
/**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.*/
4343
virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;
4444

45+
virtual void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const = 0;
46+
4547
/**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.
4648
If no layers/style are found, an empty list is returned
4749
@param allowCache true if layer can be read from / written to cache*/
@@ -87,6 +89,8 @@ class QgsConfigParser
8789

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

9195
/**Returns a set of supported epsg codes for the capabilities document. An empty list means
9296
that all possible CRS should be advertised (which could result in very long capabilities documents)*/

‎src/mapserver/qgshttprequesthandler.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,56 @@ void QgsHttpRequestHandler::sendGetPrintResponse( QByteArray* ba ) const
288288
sendHttpResponse( ba, formatToMimeType( mFormat ) );
289289
}
290290

291+
bool QgsHttpRequestHandler::startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const
292+
{
293+
if ( !ba )
294+
{
295+
return false;
296+
}
297+
298+
if ( ba->size() < 1 )
299+
{
300+
return false;
301+
}
302+
303+
QString format;
304+
if ( infoFormat == "GeoJSON" )
305+
format = "text/plain";
306+
else
307+
format = "text/xml";
308+
309+
printf( "Content-Type: " );
310+
printf( format.toLocal8Bit() );
311+
printf( "\n" );
312+
printf( "\n" );
313+
fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
314+
return true;
315+
}
316+
317+
void QgsHttpRequestHandler::sendGetFeatureResponse( QByteArray* ba ) const
318+
{
319+
if ( !ba )
320+
{
321+
return;
322+
}
323+
324+
if ( ba->size() < 1 )
325+
{
326+
return;
327+
}
328+
fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
329+
}
330+
331+
void QgsHttpRequestHandler::endGetFeatureResponse( QByteArray* ba ) const
332+
{
333+
if ( !ba )
334+
{
335+
return;
336+
}
337+
338+
fwrite( ba->data(), ba->size(), 1, FCGI_stdout );
339+
}
340+
291341
void QgsHttpRequestHandler::requestStringToParameterMap( const QString& request, QMap<QString, QString>& parameters )
292342
{
293343
parameters.clear();

‎src/mapserver/qgshttprequesthandler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class QgsHttpRequestHandler: public QgsRequestHandler
3434
virtual void sendServiceException( const QgsMapServiceException& ex ) const;
3535
virtual void sendGetStyleResponse( const QDomDocument& doc ) const;
3636
virtual void sendGetPrintResponse( QByteArray* ba ) const;
37+
virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const;
38+
virtual void sendGetFeatureResponse( QByteArray* ba ) const;
39+
virtual void endGetFeatureResponse( QByteArray* ba ) const;
3740

3841
protected:
3942
void sendHttpResponse( QByteArray* ba, const QString& format ) const;

‎src/mapserver/qgsprojectparser.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,64 @@ void QgsProjectParser::layersAndStylesCapabilities( QDomElement& parentElement,
138138
combineExtentAndCrsOfGroupChildren( layerParentElem, doc );
139139
}
140140

141+
void QgsProjectParser::featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const
142+
{
143+
QStringList wfsLayersId = wfsLayers();
144+
145+
if ( mProjectLayerElements.size() < 1 )
146+
{
147+
return;
148+
}
149+
150+
QMap<QString, QgsMapLayer *> layerMap;
151+
152+
foreach( const QDomElement &elem, mProjectLayerElements )
153+
{
154+
QString type = elem.attribute( "type" );
155+
if ( type == "vector" )
156+
{
157+
//QgsMapLayer *layer = createLayerFromElement( *layerIt );
158+
QgsMapLayer *layer = createLayerFromElement( elem );
159+
if ( layer && wfsLayersId.contains( layer->id() ) )
160+
{
161+
QgsDebugMsg( QString( "add layer %1 to map" ).arg( layer->id() ) );
162+
layerMap.insert( layer->id(), layer );
163+
164+
QDomElement layerElem = doc.createElement( "FeatureType" );
165+
QDomElement nameElem = doc.createElement( "Name" );
166+
//We use the layer name even though it might not be unique.
167+
//Because the id sometimes contains user/pw information and the name is more descriptive
168+
QDomText nameText = doc.createTextNode( layer->name() );
169+
nameElem.appendChild( nameText );
170+
layerElem.appendChild( nameElem );
171+
172+
QDomElement titleElem = doc.createElement( "Title" );
173+
QDomText titleText = doc.createTextNode( layer->name() );
174+
titleElem.appendChild( titleText );
175+
layerElem.appendChild( titleElem );
176+
177+
//appendExGeographicBoundingBox( layerElem, doc, layer->extent(), layer->crs() );
178+
179+
QDomElement srsElem = doc.createElement( "SRS" );
180+
QDomText srsText = doc.createTextNode( layer->crs().authid() );
181+
srsElem.appendChild( srsText );
182+
layerElem.appendChild( srsElem );
183+
184+
QgsRectangle layerExtent = layer->extent();
185+
QDomElement bBoxElement = doc.createElement( "LatLongBoundingBox" );
186+
bBoxElement.setAttribute( "minx", QString::number( layerExtent.xMinimum() ) );
187+
bBoxElement.setAttribute( "miny", QString::number( layerExtent.yMinimum() ) );
188+
bBoxElement.setAttribute( "maxx", QString::number( layerExtent.xMaximum() ) );
189+
bBoxElement.setAttribute( "maxy", QString::number( layerExtent.yMaximum() ) );
190+
layerElem.appendChild( bBoxElement );
191+
192+
parentElement.appendChild( layerElem );
193+
}
194+
}
195+
}
196+
return;
197+
}
198+
141199
void QgsProjectParser::addLayers( QDomDocument &doc,
142200
QDomElement &parentElem,
143201
const QDomElement &legendElem,
@@ -584,6 +642,37 @@ QStringList QgsProjectParser::identifyDisabledLayers() const
584642
return disabledList;
585643
}
586644

645+
QStringList QgsProjectParser::wfsLayers() const
646+
{
647+
QStringList wfsList;
648+
if ( !mXMLDoc )
649+
{
650+
return wfsList;
651+
}
652+
653+
QDomElement qgisElem = mXMLDoc->documentElement();
654+
if ( qgisElem.isNull() )
655+
{
656+
return wfsList;
657+
}
658+
QDomElement propertiesElem = qgisElem.firstChildElement( "properties" );
659+
if ( propertiesElem.isNull() )
660+
{
661+
return wfsList;
662+
}
663+
QDomElement wfsLayersElem = propertiesElem.firstChildElement( "WFSLayers" );
664+
if ( wfsLayersElem.isNull() )
665+
{
666+
return wfsList;
667+
}
668+
QDomNodeList valueList = wfsLayersElem.elementsByTagName( "value" );
669+
for ( int i = 0; i < valueList.size(); ++i )
670+
{
671+
wfsList << valueList.at( i ).toElement().text();
672+
}
673+
return wfsList;
674+
}
675+
587676
QStringList QgsProjectParser::supportedOutputCrsList() const
588677
{
589678
QStringList crsList;

‎src/mapserver/qgsprojectparser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class QgsProjectParser: public QgsConfigParser
3838
/**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.*/
3939
virtual void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
4040

41+
virtual void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const;
42+
4143
int numberOfLayers() const;
4244

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

63+
/**Returns an ID-list of layers queryable for WFS service (comes from <properties> -> <WFSLayers> in the project file*/
64+
virtual QStringList wfsLayers() const;
65+
6166
/**Returns a set of supported epsg codes for the capabilities document. The list comes from the property <WMSEpsgList> in the project file.
6267
An empty set means that all possible CRS should be advertised (which could result in very long capabilities documents)
6368
Example:

‎src/mapserver/qgsrequesthandler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class QgsRequestHandler
4141
virtual void sendServiceException( const QgsMapServiceException& ex ) const = 0;
4242
virtual void sendGetStyleResponse( const QDomDocument& doc ) const = 0;
4343
virtual void sendGetPrintResponse( QByteArray* ba ) const = 0;
44+
virtual bool startGetFeatureResponse( QByteArray* ba, const QString& infoFormat ) const = 0;
45+
virtual void sendGetFeatureResponse( QByteArray* ba ) const = 0;
46+
virtual void endGetFeatureResponse( QByteArray* ba ) const = 0;
4447
QString format() const { return mFormat; }
4548
protected:
4649
/**This is set by the parseInput methods of the subclasses (parameter FORMAT, e.g. 'FORMAT=PNG')*/

‎src/mapserver/qgssldparser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class QgsSLDParser: public QgsConfigParser
5656
/**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.*/
5757
void layersAndStylesCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
5858

59+
void featureTypeList( QDomElement& parentElement, QDomDocument& doc ) const {};
60+
5961
/**Returns number of layers in configuration*/
6062
int numberOfLayers() const;
6163

‎src/mapserver/qgswfsserver.cpp

Lines changed: 946 additions & 0 deletions
Large diffs are not rendered by default.

‎src/mapserver/qgswfsserver.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
2+
#ifndef QGSWFSSERVER_H
3+
#define QGSWFSSERVER_H
4+
5+
#include <QDomDocument>
6+
#include <QMap>
7+
#include <QString>
8+
#include <map>
9+
10+
class QgsCoordinateReferenceSystem;
11+
class QgsComposerLayerItem;
12+
class QgsComposerLegendItem;
13+
class QgsComposition;
14+
class QgsMapLayer;
15+
class QgsMapRenderer;
16+
class QgsPoint;
17+
class QgsRasterLayer;
18+
class QgsConfigParser;
19+
class QgsVectorLayer;
20+
class QgsCoordinateReferenceSystem;
21+
class QgsField;
22+
class QgsFeature;
23+
class QgsGeometry;
24+
class QgsSymbol;
25+
class QgsRequestHandler;
26+
class QFile;
27+
class QFont;
28+
class QImage;
29+
class QPaintDevice;
30+
class QPainter;
31+
32+
/**This class handles all the wms server requests. The parameters and values have to be passed in the form of
33+
a map<QString, QString>. This map is usually generated by a subclass of QgsWMSRequestHandler, which makes QgsWFSServer
34+
independent from any server side technology*/
35+
36+
class QgsWFSServer
37+
{
38+
public:
39+
/**Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
40+
QgsWFSServer( QMap<QString, QString> parameters );
41+
~QgsWFSServer();
42+
/**Returns an XML file with the capabilities description (as described in the WFS specs)*/
43+
QDomDocument getCapabilities();
44+
45+
/**Returns an XML file with the describe feature type (as described in the WFS specs)*/
46+
QDomDocument describeFeatureType();
47+
48+
/**Creates a document that describes the result of the getFeature request.
49+
@return 0 in case of success*/
50+
int getFeature( QgsRequestHandler& request, const QString& format );
51+
52+
/**Sets configuration parser for administration settings. Does not take ownership*/
53+
void setAdminConfigParser( QgsConfigParser* parser ) { mConfigParser = parser; }
54+
55+
private:
56+
/**Don't use the default constructor*/
57+
QgsWFSServer();
58+
59+
/**Map containing the WMS parameters*/
60+
QMap<QString, QString> mParameterMap;
61+
QgsConfigParser* mConfigParser;
62+
QString mTypeName;
63+
bool mWithGeom;
64+
65+
protected:
66+
67+
void startGetFeature( QgsRequestHandler& request, const QString& format );
68+
void sendGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes );
69+
void endGetFeature( QgsRequestHandler& request, const QString& format );
70+
71+
//methods to write GeoJSON
72+
QString createFeatureGeoJSON( QgsFeature* feat, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;
73+
74+
//methods to write GML2
75+
QDomElement createFeatureElem( QgsFeature* feat, QDomDocument& doc, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;
76+
77+
QDomElement createGeometryElem( QgsGeometry* g, QDomDocument& doc ) /*const*/;
78+
QDomElement createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
79+
QDomElement createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
80+
QDomElement createPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
81+
QDomElement createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
82+
QDomElement createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
83+
QDomElement createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
84+
85+
/**Create a GML coordinate string from a point list.
86+
@param points list of data points
87+
@param coordString out: GML coord string
88+
@return 0 in case of success*/
89+
QDomElement createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const;
90+
};
91+
92+
#endif

‎src/ui/qgsprojectpropertiesbase.ui

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,9 @@
363363
</item>
364364
</layout>
365365
</widget>
366-
<widget class="QWidget" name="tab">
366+
<widget class="QWidget" name="tab4">
367367
<attribute name="title">
368-
<string>WMS Server</string>
368+
<string>OWS Server</string>
369369
</attribute>
370370
<layout class="QGridLayout" name="gridLayout_3">
371371
<item row="0" column="0" rowspan="2">
@@ -384,7 +384,7 @@
384384
</property>
385385
<layout class="QGridLayout" name="gridLayout_7">
386386
<item row="0" column="0" colspan="2">
387-
<widget class="QGroupBox" name="grpWMSServiceCapabilities">
387+
<widget class="QGroupBox" name="grpOWSServiceCapabilities">
388388
<property name="title">
389389
<string>Service Capabilitities</string>
390390
</property>
@@ -483,6 +483,12 @@
483483
</layout>
484484
</widget>
485485
</item>
486+
<item row="1" column="0" colspan="2">
487+
<widget class="QGroupBox" name="grpWMSCapabilities">
488+
<property name="title">
489+
<string>WMS Capabilitities</string>
490+
</property>
491+
<layout class="QGridLayout" name="gridLayout_7">
486492
<item row="1" column="0">
487493
<widget class="QGroupBox" name="grpWMSExt">
488494
<property name="title">
@@ -631,6 +637,32 @@
631637
<string>Add WKT geometry to feature info response</string>
632638
</property>
633639
</widget>
640+
</item>
641+
</layout>
642+
</widget>
643+
</item>
644+
<item row="3" column="0" colspan="2">
645+
<widget class="QGroupBox" name="grpWFSCapabilities">
646+
<property name="title">
647+
<string>WFS Capabilitities</string>
648+
</property>
649+
<layout class="QGridLayout" name="gridLayout_8">
650+
<item row="0" column="0">
651+
<widget class="QTableWidget" name="twWFSLayers">
652+
<column>
653+
<property name="text">
654+
<string>Layer</string>
655+
</property>
656+
</column>
657+
<column>
658+
<property name="text">
659+
<string>Published</string>
660+
</property>
661+
</column>
662+
</widget>
663+
</item>
664+
</layout>
665+
</widget>
634666
</item>
635667
</layout>
636668
</widget>

0 commit comments

Comments
 (0)
Please sign in to comment.