22
22
#include " qgslogger.h"
23
23
#include < QDomDocument>
24
24
#include < QDomNodeList>
25
+ #include < QFile>
25
26
#include < cfloat>
26
27
27
28
#ifdef WIN32
@@ -248,59 +249,57 @@ void QgsWFSProvider::select(QgsRect *mbr, bool useIntersect)
248
249
249
250
int QgsWFSProvider::getFeature (const QString& uri)
250
251
{
251
- // GET or SOAP?
252
- if (uri.startsWith (" SOAP://" ))
252
+ QString geometryAttribute;
253
+
254
+ // Local url or HTTP?
255
+ if (uri.startsWith (" http://" ))
253
256
{
254
- mEncoding = QgsWFSProvider::SOAP ;
257
+ mEncoding = QgsWFSProvider::GET ;
255
258
}
256
259
else
257
260
{
258
- mEncoding = QgsWFSProvider::GET ;
261
+ mEncoding = QgsWFSProvider::FILE ;
259
262
}
260
263
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)
272
265
{
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
+ }
281
271
}
282
-
283
- if (mEncoding == QgsWFSProvider::SOAP)
272
+ else // take schema with describeFeatureType request
284
273
{
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
+ }
286
280
}
287
- else
281
+
282
+ if (mEncoding == QgsWFSProvider::GET)
288
283
{
289
284
return getFeatureGET (uri, geometryAttribute);
290
285
}
286
+ else // local file
287
+ {
288
+ return getFeatureFILE (uri, geometryAttribute); // read the features from disk
289
+ }
291
290
return 2 ;
292
291
}
293
292
294
- int QgsWFSProvider::describeFeatureType (const QString& uri, std::vector<QgsField>& fields)
293
+ int QgsWFSProvider::describeFeatureType (const QString& uri, QString& geometryAttribute, std::vector<QgsField>& fields)
295
294
{
296
295
switch (mEncoding )
297
296
{
298
297
case QgsWFSProvider::GET:
299
- return describeFeatureTypeGET (uri, fields);
298
+ return describeFeatureTypeGET (uri, geometryAttribute, fields);
300
299
case QgsWFSProvider::POST:
301
- return describeFeatureTypePOST (uri, fields);
300
+ return describeFeatureTypePOST (uri, geometryAttribute, fields);
302
301
case QgsWFSProvider::SOAP:
303
- return describeFeatureTypeSOAP (uri, fields);
302
+ return describeFeatureTypeSOAP (uri, geometryAttribute, fields);
304
303
}
305
304
return 1 ;
306
305
}
@@ -347,57 +346,128 @@ int QgsWFSProvider::getFeatureSOAP(const QString& uri, const QString& geometryAt
347
346
return 1 ; // soon...
348
347
}
349
348
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)
351
385
{
352
386
QByteArray result;
353
387
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
+ }
355
411
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))
360
426
{
361
- if (tnamelist.at (i).startsWith (" typename" , Qt::CaseInsensitive))
427
+ QDomDocument schemaDoc;
428
+ if (!schemaDoc.setContent (&schemaFile, true ))
362
429
{
363
- QStringList tlist = tnamelist.at (i).split (" =" );
364
- tname = tlist.at (1 );
430
+ return 1 ; // xml file not readable or not valid
365
431
}
432
+
433
+ if (readAttributesFromSchema (schemaDoc, geometryAttribute, fields) != 0 )
434
+ {
435
+ return 2 ;
436
+ }
437
+ return 0 ;
366
438
}
439
+
440
+ std::list<QString> thematicAttributes;
367
441
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 )
370
444
{
371
- tname = tname. section ( " : " , 1 , 1 ) ;
445
+ return 1 ;
372
446
}
373
447
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 )
376
450
{
377
- return 1 ; // error
451
+ fields. push_back ( QgsField (*it, " unknown " ));
378
452
}
453
+ return 0 ;
454
+ }
379
455
380
- qWarning (describeFeatureDocument. toString ());
381
-
456
+ int QgsWFSProvider::readAttributesFromSchema (QDomDocument& schemaDoc, QString& geometryAttribute, std::vector<QgsField>& fields) const
457
+ {
382
458
// 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" );
384
460
if (schemaNodeList.length () < 1 )
385
461
{
386
- return 2 ;
462
+ return 1 ;
387
463
}
388
464
QDomElement schemaElement = schemaNodeList.at (0 ).toElement ();
389
465
390
466
// find <element name="tname" type = ...>
391
467
QString complexTypeType;
392
468
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" );
401
471
402
472
if (complexTypeType.isEmpty ())
403
473
{
@@ -418,6 +488,7 @@ int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, std::vector<QgsFi
418
488
if (complexTypeNodeList.at (i).toElement ().attribute (" name" ) == complexTypeType)
419
489
{
420
490
complexTypeElement = complexTypeNodeList.at (i).toElement ();
491
+ break ;
421
492
}
422
493
}
423
494
@@ -428,31 +499,94 @@ int QgsWFSProvider::describeFeatureTypeGET(const QString& uri, std::vector<QgsFi
428
499
429
500
// now create the attributes
430
501
QDomNodeList attributeNodeList = complexTypeElement.elementsByTagNameNS (" http://www.w3.org/2001/XMLSchema" , " element" );
502
+ if (attributeNodeList.size () < 1 )
503
+ {
504
+ return 5 ;
505
+ }
506
+
431
507
for (int i = 0 ; i < attributeNodeList.length (); ++i)
432
508
{
433
509
QDomElement attributeElement = attributeNodeList.at (i).toElement ();
434
510
// attribute name
435
511
QString name = attributeElement.attribute (" name" );
436
512
// attribute type
437
513
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
439
521
{
440
- // todo: is the type name inside a <simpleType> element?
522
+ fields. push_back ( QgsField ( name, type));
441
523
}
442
- // todo: distinguish between numerical and non-numerical types
443
- fields.push_back (QgsField (name, type));
444
524
}
445
525
return 0 ;
446
526
}
447
527
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
449
529
{
450
- return 1 ; // soon...
451
- }
530
+ QFile gmlFile (uri);
531
+ if (!gmlFile.open (QIODevice::ReadOnly))
532
+ {
533
+ return 1 ;
534
+ }
452
535
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 ;
456
590
}
457
591
458
592
int QgsWFSProvider::getExtentFromGML2 (QgsRect* extent, const QDomElement& wfsCollectionElement) const
0 commit comments