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
249250int 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
458592int QgsWFSProvider::getExtentFromGML2 (QgsRect* extent, const QDomElement& wfsCollectionElement) const
0 commit comments