Skip to content

Commit 33b74e6

Browse files

File tree

2 files changed

+221
-74
lines changed

2 files changed

+221
-74
lines changed
 

‎src/providers/wfs/qgswfsprovider.cpp

Lines changed: 202 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "qgslogger.h"
2323
#include <QDomDocument>
2424
#include <QDomNodeList>
25+
#include <QFile>
2526
#include <cfloat>
2627

2728
#ifdef WIN32
@@ -248,59 +249,57 @@ void QgsWFSProvider::select(QgsRect *mbr, bool useIntersect)
248249

249250
int QgsWFSProvider::getFeature(const QString& uri)
250251
{
251-
//GET or SOAP?
252-
if(uri.startsWith("SOAP://"))
252+
QString geometryAttribute;
253+
254+
//Local url or HTTP?
255+
if(uri.startsWith("http://"))
253256
{
254-
mEncoding = QgsWFSProvider::SOAP;
257+
mEncoding = QgsWFSProvider::GET;
255258
}
256259
else
257260
{
258-
mEncoding = QgsWFSProvider::GET;
261+
mEncoding = QgsWFSProvider::FILE;
259262
}
260263

261-
QString describeFeatureUri = uri;
262-
describeFeatureUri.replace(QString("GetFeature"), QString("DescribeFeatureType"));
263-
if(describeFeatureType(describeFeatureUri, mFields) != 0)
264-
{
265-
return 1;
266-
}
267-
268-
//find out the name of the attribute containing the geometry
269-
QString geometryAttribute;
270-
QString currentAttribute;
271-
for(std::vector<QgsField>::iterator iter = mFields.begin(); iter != mFields.end(); ++iter)
264+
if(mEncoding == QgsWFSProvider::FILE)
272265
{
273-
currentAttribute = iter->type();
274-
if(currentAttribute.startsWith("gml:") && currentAttribute.endsWith("PropertyType"))
275-
{
276-
geometryAttribute = iter->name();
277-
//erase the geometry attribute from mFields (QGIS distinguishes between geometry and thematic attributes)
278-
mFields.erase(iter);
279-
break;
280-
}
266+
//guess geometry attribute and other attributes from schema or from .gml file
267+
if(describeFeatureTypeFile(uri, geometryAttribute, mFields) != 0)
268+
{
269+
return 1;
270+
}
281271
}
282-
283-
if(mEncoding == QgsWFSProvider::SOAP)
272+
else //take schema with describeFeatureType request
284273
{
285-
return getFeatureSOAP(uri, geometryAttribute);
274+
QString describeFeatureUri = uri;
275+
describeFeatureUri.replace(QString("GetFeature"), QString("DescribeFeatureType"));
276+
if(describeFeatureType(describeFeatureUri, geometryAttribute, mFields) != 0)
277+
{
278+
return 1;
279+
}
286280
}
287-
else
281+
282+
if(mEncoding == QgsWFSProvider::GET)
288283
{
289284
return getFeatureGET(uri, geometryAttribute);
290285
}
286+
else//local file
287+
{
288+
return getFeatureFILE(uri, geometryAttribute); //read the features from disk
289+
}
291290
return 2;
292291
}
293292

294-
int QgsWFSProvider::describeFeatureType(const QString& uri, std::vector<QgsField>& fields)
293+
int QgsWFSProvider::describeFeatureType(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
295294
{
296295
switch(mEncoding)
297296
{
298297
case QgsWFSProvider::GET:
299-
return describeFeatureTypeGET(uri, fields);
298+
return describeFeatureTypeGET(uri, geometryAttribute, fields);
300299
case QgsWFSProvider::POST:
301-
return describeFeatureTypePOST(uri, fields);
300+
return describeFeatureTypePOST(uri, geometryAttribute, fields);
302301
case QgsWFSProvider::SOAP:
303-
return describeFeatureTypeSOAP(uri, fields);
302+
return describeFeatureTypeSOAP(uri, geometryAttribute, fields);
304303
}
305304
return 1;
306305
}
@@ -347,57 +346,128 @@ int QgsWFSProvider::getFeatureSOAP(const QString& uri, const QString& geometryAt
347346
return 1; //soon...
348347
}
349348

350-
int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, std::vector<QgsField>& fields)
349+
int QgsWFSProvider::getFeatureFILE(const QString& uri, const QString& geometryAttribute)
350+
{
351+
QFile gmlFile(uri);
352+
if(!gmlFile.open(QIODevice::ReadOnly))
353+
{
354+
mValid = false;
355+
return 1;
356+
}
357+
358+
QDomDocument gmlDoc;
359+
QString errorMsg;
360+
int errorLine, errorColumn;
361+
if(!gmlDoc.setContent(&gmlFile, true, &errorMsg, &errorLine, &errorColumn))
362+
{
363+
mValid = false;
364+
return 2;
365+
}
366+
367+
QDomElement featureCollectionElement = gmlDoc.documentElement();
368+
//get and set Extent
369+
if(getExtentFromGML2(&mExtent, featureCollectionElement) != 0)
370+
{
371+
return 3;
372+
}
373+
374+
setSRSFromGML2(featureCollectionElement);
375+
376+
if(getFeaturesFromGML2(featureCollectionElement, geometryAttribute) != 0)
377+
{
378+
return 4;
379+
}
380+
381+
return 0;
382+
}
383+
384+
int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
351385
{
352386
QByteArray result;
353387
QgsHttpTransaction http(uri);
354-
http.getSynchronously(result);
388+
if(!http.getSynchronously(result))
389+
{
390+
return 1;
391+
}
392+
QDomDocument describeFeatureDocument;
393+
394+
if(!describeFeatureDocument.setContent(result, true))
395+
{
396+
return 2;
397+
}
398+
399+
if(readAttributesFromSchema(describeFeatureDocument, geometryAttribute, fields) != 0)
400+
{
401+
return 3;
402+
}
403+
404+
return 0;
405+
}
406+
407+
int QgsWFSProvider::describeFeatureTypePOST(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
408+
{
409+
return 1; //soon...
410+
}
355411

356-
//find out the typename
357-
QString tname;
358-
QStringList tnamelist = uri.split("&");
359-
for(int i = 0; i < tnamelist.size(); ++i)
412+
int QgsWFSProvider::describeFeatureTypeSOAP(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
413+
{
414+
return 1; //soon...
415+
}
416+
417+
int QgsWFSProvider::describeFeatureTypeFile(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
418+
{
419+
//first look in the schema file
420+
QString noExtension = uri;
421+
noExtension.chop(3);
422+
QString schemaUri = noExtension.append("xsd");
423+
QFile schemaFile(schemaUri);
424+
425+
if(schemaFile.open(QIODevice::ReadOnly))
360426
{
361-
if(tnamelist.at(i).startsWith("typename", Qt::CaseInsensitive))
427+
QDomDocument schemaDoc;
428+
if(!schemaDoc.setContent(&schemaFile, true))
362429
{
363-
QStringList tlist = tnamelist.at(i).split("=");
364-
tname = tlist.at(1);
430+
return 1; //xml file not readable or not valid
365431
}
432+
433+
if(readAttributesFromSchema(schemaDoc, geometryAttribute, fields) != 0)
434+
{
435+
return 2;
436+
}
437+
return 0;
366438
}
439+
440+
std::list<QString> thematicAttributes;
367441

368-
//remove the namespace from tname
369-
if(tname.contains(":"))
442+
//if this fails (e.g. no schema file), try to guess the geometry attribute and the names of the thematic attributes from the .gml file
443+
if(guessAttributesFromFile(uri, geometryAttribute, thematicAttributes) != 0)
370444
{
371-
tname = tname.section(":", 1, 1);
445+
return 1;
372446
}
373447

374-
QDomDocument describeFeatureDocument;
375-
if(!describeFeatureDocument.setContent(result, true))
448+
fields.clear();
449+
for(std::list<QString>::const_iterator it = thematicAttributes.begin(); it != thematicAttributes.end(); ++it)
376450
{
377-
return 1; //error
451+
fields.push_back(QgsField(*it, "unknown"));
378452
}
453+
return 0;
454+
}
379455

380-
qWarning(describeFeatureDocument.toString());
381-
456+
int QgsWFSProvider::readAttributesFromSchema(QDomDocument& schemaDoc, QString& geometryAttribute, std::vector<QgsField>& fields) const
457+
{
382458
//get the <schema> root element
383-
QDomNodeList schemaNodeList = describeFeatureDocument.elementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "schema");
459+
QDomNodeList schemaNodeList = schemaDoc.elementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "schema");
384460
if(schemaNodeList.length() < 1)
385461
{
386-
return 2;
462+
return 1;
387463
}
388464
QDomElement schemaElement = schemaNodeList.at(0).toElement();
389465

390466
//find <element name="tname" type = ...>
391467
QString complexTypeType;
392468
QDomNodeList typeElementNodeList = schemaElement.elementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "element");
393-
for(int i = 0; i < typeElementNodeList.length(); ++i)
394-
{
395-
QDomElement typeElement = typeElementNodeList.at(i).toElement();
396-
if(typeElement.attribute("name") == tname)
397-
{
398-
complexTypeType = typeElement.attribute("type");
399-
}
400-
}
469+
QDomElement typeElement = typeElementNodeList.at(0).toElement();
470+
complexTypeType = typeElement.attribute("type");
401471

402472
if(complexTypeType.isEmpty())
403473
{
@@ -418,6 +488,7 @@ int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, std::vector<QgsFi
418488
if(complexTypeNodeList.at(i).toElement().attribute("name") == complexTypeType)
419489
{
420490
complexTypeElement = complexTypeNodeList.at(i).toElement();
491+
break;
421492
}
422493
}
423494

@@ -428,31 +499,94 @@ int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, std::vector<QgsFi
428499

429500
//now create the attributes
430501
QDomNodeList attributeNodeList = complexTypeElement.elementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "element");
502+
if(attributeNodeList.size() < 1)
503+
{
504+
return 5;
505+
}
506+
431507
for(int i = 0; i < attributeNodeList.length(); ++i)
432508
{
433509
QDomElement attributeElement = attributeNodeList.at(i).toElement();
434510
//attribute name
435511
QString name = attributeElement.attribute("name");
436512
//attribute type
437513
QString type = attributeElement.attribute("type");
438-
if(type.isEmpty())
514+
515+
//is it a geometry attribute?
516+
if(type.startsWith("gml:") && type.endsWith("PropertyType"))
517+
{
518+
geometryAttribute = name;
519+
}
520+
else //todo: distinguish between numerical and non-numerical types
439521
{
440-
//todo: is the type name inside a <simpleType> element?
522+
fields.push_back(QgsField(name, type));
441523
}
442-
//todo: distinguish between numerical and non-numerical types
443-
fields.push_back(QgsField(name, type));
444524
}
445525
return 0;
446526
}
447527

448-
int QgsWFSProvider::describeFeatureTypePOST(const QString& uri, std::vector<QgsField>& fields)
528+
int QgsWFSProvider::guessAttributesFromFile(const QString& uri, QString& geometryAttribute, std::list<QString>& thematicAttributes) const
449529
{
450-
return 1; //soon...
451-
}
530+
QFile gmlFile(uri);
531+
if(!gmlFile.open(QIODevice::ReadOnly))
532+
{
533+
return 1;
534+
}
452535

453-
int QgsWFSProvider::describeFeatureTypeSOAP(const QString& uri, std::vector<QgsField>& fields)
454-
{
455-
return 1; //soon...
536+
QDomDocument gmlDoc;
537+
if(!gmlDoc.setContent(&gmlFile, true))
538+
{
539+
return 2; //xml file not readable or not valid
540+
}
541+
542+
543+
//find gmlCollection element
544+
QDomElement featureCollectionElement = gmlDoc.documentElement();
545+
546+
//get the first feature to guess attributes
547+
QDomNodeList featureList = featureCollectionElement.elementsByTagNameNS(GML_NAMESPACE, "featureMember");
548+
if(featureList.size() < 1)
549+
{
550+
return 3; //we need at least one attribute
551+
}
552+
553+
QDomElement featureElement = featureList.at(0).toElement();
554+
QDomNode attributeNode = featureElement.firstChild().firstChild();
555+
if(attributeNode.isNull())
556+
{
557+
return 4;
558+
}
559+
QString attributeText;
560+
QDomElement attributeChildElement;
561+
QString attributeChildLocalName;
562+
563+
while(!attributeNode.isNull())//loop over attributes
564+
{
565+
QString attributeNodeName = attributeNode.toElement().localName();
566+
attributeChildElement = attributeNode.firstChild().toElement();
567+
if(attributeChildElement.isNull())//no child element means it is a thematic attribute for sure
568+
{
569+
thematicAttributes.push_back(attributeNode.toElement().localName());
570+
attributeNode = attributeNode.nextSibling();
571+
continue;
572+
}
573+
574+
attributeChildLocalName = attributeChildElement.localName();
575+
if(attributeChildLocalName == "Point" || attributeChildLocalName == "LineString" || \
576+
attributeChildLocalName == "Polygon" || attributeChildLocalName == "MultiPoint" || \
577+
attributeChildLocalName == "MultiLineString" || attributeChildLocalName == "MultiPolygon" || \
578+
attributeChildLocalName == "Surface" || attributeChildLocalName == "MultiSurface")
579+
{
580+
geometryAttribute = attributeNode.toElement().localName(); //a geometry attribute
581+
}
582+
else
583+
{
584+
thematicAttributes.push_back(attributeNode.toElement().localName()); //a thematic attribute
585+
}
586+
attributeNode = attributeNode.nextSibling();
587+
}
588+
589+
return 0;
456590
}
457591

458592
int QgsWFSProvider::getExtentFromGML2(QgsRect* extent, const QDomElement& wfsCollectionElement) const

‎src/providers/wfs/qgswfsprovider.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class QgsWFSProvider: public QgsVectorDataProvider
3535
{
3636
GET,
3737
POST,
38-
SOAP /*Note that this goes also through HTTP POST but additionally uses soap envelope and friends*/
38+
SOAP,/*Note that this goes also through HTTP POST but additionally uses soap envelope and friends*/
39+
FILE //reads from a file on disk
3940
};
4041

4142
QgsWFSProvider(const QString& uri);
@@ -105,17 +106,24 @@ class QgsWFSProvider: public QgsVectorDataProvider
105106
/**Goes through all the features and their attributes and populates mMinMaxCash with entries*/
106107
void fillMinMaxCash();
107108

108-
/**Collects information about the field types. Is called internally from QgsWFSProvider::getFeature*/
109-
int describeFeatureType(const QString& uri, std::vector<QgsField>& fields);
109+
/**Collects information about the field types. Is called internally from QgsWFSProvider::getFeature. The method delegates the work to request specific ones and gives back the name of the geometry attribute and the thematic attributes with their types*/
110+
int describeFeatureType(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields);
110111

111112
//encoding specific methods of getFeature
112113
int getFeatureGET(const QString& uri, const QString& geometryAttribute);
113114
int getFeaturePOST(const QString& uri, const QString& geometryAttribute);
114115
int getFeatureSOAP(const QString& uri, const QString& geometryAttribute);
116+
int getFeatureFILE(const QString& uri, const QString& geometryAttribute);
115117
//encoding specific methods of describeFeatureType
116-
int describeFeatureTypeGET(const QString& uri, std::vector<QgsField>& fields);
117-
int describeFeatureTypePOST(const QString& uri, std::vector<QgsField>& fields);
118-
int describeFeatureTypeSOAP(const QString& uri, std::vector<QgsField>& fields);
118+
int describeFeatureTypeGET(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields);
119+
int describeFeatureTypePOST(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields);
120+
int describeFeatureTypeSOAP(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields);
121+
int describeFeatureTypeFile(const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields);
122+
123+
/**Reads the name of the geometry attribute, the thematic attributes and their types from a dom document. Returns 0 in case of success*/
124+
int readAttributesFromSchema(QDomDocument& schemaDoc, QString& geometryAttribute, std::vector<QgsField>& fields) const;
125+
/**This method tries to guess the geometry attribute and the other attribute names from the .gml file if no schema is present. Returns 0 in case of success*/
126+
int guessAttributesFromFile(const QString& uri, QString& geometryAttribute, std::list<QString>& thematicAttributes) const;
119127

120128
//GML2 specific methods
121129
int getExtentFromGML2(QgsRect* extent, const QDomElement& wfsCollectionElement) const;
@@ -143,6 +151,11 @@ class QgsWFSProvider: public QgsVectorDataProvider
143151
/**Tries to create a QgsSpatialRefSys object and assign it to mSourceSRS. Returns 0 in case of success*/
144152
int setSRSFromGML2(const QDomElement& wfsCollectionElement);
145153

154+
155+
156+
157+
158+
146159
//GML3 specific methods. Not needed at the moment as most servers support GML2
147160
#if 0
148161
/**Evaluates the <gml:boundedBy> element

0 commit comments

Comments
 (0)
Please sign in to comment.