QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsgml.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgml.cpp
3  ---------------------
4  begin : February 2013
5  copyright : (C) 2013 by Radim Blazek
6  email : radim dot blazek at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsgml.h"
16 #include "qgsauthmanager.h"
17 #include "qgsrectangle.h"
19 #include "qgsgeometry.h"
20 #include "qgslogger.h"
21 #include "qgsmessagelog.h"
23 #include "qgswkbptr.h"
24 #include "qgsogrutils.h"
25 #include "qgsapplication.h"
26 #include <QBuffer>
27 #include <QList>
28 #include <QNetworkRequest>
29 #include <QNetworkReply>
30 #include <QProgressDialog>
31 #include <QSet>
32 #include <QSettings>
33 #include <QUrl>
34 
35 #include "ogr_api.h"
36 
37 #include <limits>
38 
39 static const char NS_SEPARATOR = '?';
40 static const char *GML_NAMESPACE = "http://www.opengis.net/gml";
41 static const char *GML32_NAMESPACE = "http://www.opengis.net/gml/3.2";
42 
44  const QString &typeName,
45  const QString &geometryAttribute,
46  const QgsFields &fields )
47  : mParser( typeName, geometryAttribute, fields )
48  , mTypeName( typeName )
49  , mFinished( false )
50 {
51  int index = mTypeName.indexOf( ':' );
52  if ( index != -1 && index < mTypeName.length() )
53  {
54  mTypeName = mTypeName.mid( index + 1 );
55  }
56 }
57 
58 int QgsGml::getFeatures( const QString &uri, QgsWkbTypes::Type *wkbType, QgsRectangle *extent, const QString &userName, const QString &password, const QString &authcfg )
59 {
60  //start with empty extent
61  mExtent.setMinimal();
62 
63  QNetworkRequest request( uri );
64  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsGml" ) );
65 
66  if ( !authcfg.isEmpty() )
67  {
68  if ( !QgsApplication::authManager()->updateNetworkRequest( request, authcfg ) )
69  {
71  tr( "GML Getfeature network request update failed for authcfg %1" ).arg( authcfg ),
72  tr( "Network" ),
74  );
75  return 1;
76  }
77  }
78  else if ( !userName.isNull() || !password.isNull() )
79  {
80  request.setRawHeader( "Authorization", "Basic " + QStringLiteral( "%1:%2" ).arg( userName, password ).toLatin1().toBase64() );
81  }
82  QNetworkReply *reply = QgsNetworkAccessManager::instance()->get( request );
83 
84  if ( !authcfg.isEmpty() )
85  {
86  if ( !QgsApplication::authManager()->updateNetworkReply( reply, authcfg ) )
87  {
88  reply->deleteLater();
90  tr( "GML Getfeature network reply update failed for authcfg %1" ).arg( authcfg ),
91  tr( "Network" ),
93  );
94  return 1;
95  }
96  }
97 
98  connect( reply, &QNetworkReply::finished, this, &QgsGml::setFinished );
99  connect( reply, &QNetworkReply::downloadProgress, this, &QgsGml::handleProgressEvent );
100 
101  //find out if there is a QGIS main window. If yes, display a progress dialog
102  QProgressDialog *progressDialog = nullptr;
103  QWidget *mainWindow = nullptr;
104  QWidgetList topLevelWidgets = qApp->topLevelWidgets();
105  for ( QWidgetList::const_iterator it = topLevelWidgets.constBegin(); it != topLevelWidgets.constEnd(); ++it )
106  {
107  if ( ( *it )->objectName() == QLatin1String( "QgisApp" ) )
108  {
109  mainWindow = *it;
110  break;
111  }
112  }
113  if ( mainWindow )
114  {
115  progressDialog = new QProgressDialog( tr( "Loading GML data\n%1" ).arg( mTypeName ), tr( "Abort" ), 0, 0, mainWindow );
116  progressDialog->setWindowModality( Qt::ApplicationModal );
117  connect( this, &QgsGml::dataReadProgress, progressDialog, &QProgressDialog::setValue );
118  connect( this, &QgsGml::totalStepsUpdate, progressDialog, &QProgressDialog::setMaximum );
119  connect( progressDialog, &QProgressDialog::canceled, this, &QgsGml::setFinished );
120  progressDialog->show();
121  }
122 
123  int atEnd = 0;
124  while ( !atEnd )
125  {
126  if ( mFinished )
127  {
128  atEnd = 1;
129  }
130  QByteArray readData = reply->readAll();
131  if ( !readData.isEmpty() )
132  {
133  QString errorMsg;
134  if ( !mParser.processData( readData, atEnd, errorMsg ) )
135  QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
136 
137  }
138  QCoreApplication::processEvents();
139  }
140 
141  fillMapsFromParser();
142 
143  QNetworkReply::NetworkError replyError = reply->error();
144  QString replyErrorString = reply->errorString();
145 
146  delete reply;
147  delete progressDialog;
148 
149  if ( replyError )
150  {
152  tr( "GML Getfeature network request failed with error: %1" ).arg( replyErrorString ),
153  tr( "Network" ),
155  );
156  return 1;
157  }
158 
159  *wkbType = mParser.wkbType();
160 
161  if ( *wkbType != QgsWkbTypes::Unknown )
162  {
163  if ( mExtent.isEmpty() )
164  {
165  //reading of bbox from the server failed, so we calculate it less efficiently by evaluating the features
166  calculateExtentFromFeatures();
167  }
168  }
169 
170  if ( extent )
171  *extent = mExtent;
172 
173  return 0;
174 }
175 
176 int QgsGml::getFeatures( const QByteArray &data, QgsWkbTypes::Type *wkbType, QgsRectangle *extent )
177 {
178  mExtent.setMinimal();
179 
180  QString errorMsg;
181  if ( !mParser.processData( data, true /* atEnd */, errorMsg ) )
182  QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
183 
184  fillMapsFromParser();
185 
186  *wkbType = mParser.wkbType();
187 
188  if ( extent )
189  *extent = mExtent;
190 
191  return 0;
192 }
193 
194 void QgsGml::fillMapsFromParser()
195 {
196  QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = mParser.getAndStealReadyFeatures();
197  const auto constFeatures = features;
198  for ( const QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair &featPair : constFeatures )
199  {
200  QgsFeature *feat = featPair.first;
201  const QString &gmlId = featPair.second;
202  mFeatures.insert( feat->id(), feat );
203  if ( !gmlId.isEmpty() )
204  {
205  mIdMap.insert( feat->id(), gmlId );
206  }
207  }
208 }
209 
210 void QgsGml::setFinished()
211 {
212  mFinished = true;
213 }
214 
215 void QgsGml::handleProgressEvent( qint64 progress, qint64 totalSteps )
216 {
217  if ( totalSteps < 0 )
218  {
219  totalSteps = 0;
220  progress = 0;
221  }
222  emit totalStepsUpdate( totalSteps );
223  emit dataReadProgress( progress );
224  emit dataProgressAndSteps( progress, totalSteps );
225 }
226 
227 void QgsGml::calculateExtentFromFeatures()
228 {
229  if ( mFeatures.empty() )
230  {
231  return;
232  }
233 
234  QgsFeature *currentFeature = nullptr;
235  QgsGeometry currentGeometry;
236  bool bboxInitialized = false; //gets true once bbox has been set to the first geometry
237 
238  for ( int i = 0; i < mFeatures.size(); ++i )
239  {
240  currentFeature = mFeatures[i];
241  if ( !currentFeature )
242  {
243  continue;
244  }
245  currentGeometry = currentFeature->geometry();
246  if ( !currentGeometry.isNull() )
247  {
248  if ( !bboxInitialized )
249  {
250  mExtent = currentGeometry.boundingBox();
251  bboxInitialized = true;
252  }
253  else
254  {
255  mExtent.combineExtentWith( currentGeometry.boundingBox() );
256  }
257  }
258  }
259 }
260 
262 {
264  if ( mParser.getEPSGCode() != 0 )
265  {
266  crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( mParser.getEPSGCode() ) );
267  }
268  return crs;
269 }
270 
271 
272 
273 
274 
276  const QString &geometryAttribute,
277  const QgsFields &fields,
278  AxisOrientationLogic axisOrientationLogic,
279  bool invertAxisOrientation )
280  : mTypeName( typeName )
281  , mTypeNameBA( mTypeName.toUtf8() )
282  , mTypeNamePtr( mTypeNameBA.constData() )
283  , mTypeNameUTF8Len( strlen( mTypeNamePtr ) )
284  , mWkbType( QgsWkbTypes::Unknown )
285  , mGeometryAttribute( geometryAttribute )
286  , mGeometryAttributeBA( geometryAttribute.toUtf8() )
287  , mGeometryAttributePtr( mGeometryAttributeBA.constData() )
288  , mGeometryAttributeUTF8Len( strlen( mGeometryAttributePtr ) )
289  , mFields( fields )
290  , mIsException( false )
291  , mTruncatedResponse( false )
292  , mParseDepth( 0 )
293  , mFeatureTupleDepth( 0 )
294  , mFeatureCount( 0 )
295  , mCurrentWKB( nullptr, 0 )
296  , mBoundedByNullFound( false )
297  , mDimension( 0 )
298  , mCoorMode( Coordinate )
299  , mEpsg( 0 )
300  , mAxisOrientationLogic( axisOrientationLogic )
301  , mInvertAxisOrientationRequest( invertAxisOrientation )
302  , mInvertAxisOrientation( invertAxisOrientation )
303  , mNumberReturned( -1 )
304  , mNumberMatched( -1 )
305  , mFoundUnhandledGeometryElement( false )
306 {
307  mThematicAttributes.clear();
308  for ( int i = 0; i < fields.size(); i++ )
309  {
310  mThematicAttributes.insert( fields.at( i ).name(), qMakePair( i, fields.at( i ) ) );
311  }
312 
313  mEndian = QgsApplication::endian();
314 
315  int index = mTypeName.indexOf( ':' );
316  if ( index != -1 && index < mTypeName.length() )
317  {
318  mTypeName = mTypeName.mid( index + 1 );
319  mTypeNameBA = mTypeName.toUtf8();
320  mTypeNamePtr = mTypeNameBA.constData();
321  mTypeNameUTF8Len = strlen( mTypeNamePtr );
322  }
323 
324  mParser = XML_ParserCreateNS( nullptr, NS_SEPARATOR );
325  XML_SetUserData( mParser, this );
326  XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
327  XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
328 }
329 
330 static QString stripNS( const QString &string )
331 {
332  int index = string.indexOf( ':' );
333  if ( index != -1 && index < string.length() )
334  {
335  return string.mid( index + 1 );
336  }
337  return string;
338 }
339 
340 QgsGmlStreamingParser::QgsGmlStreamingParser( const QList<LayerProperties> &layerProperties,
341  const QgsFields &fields,
342  const QMap< QString, QPair<QString, QString> > &mapFieldNameToSrcLayerNameFieldName,
343  AxisOrientationLogic axisOrientationLogic,
344  bool invertAxisOrientation )
345  : mLayerProperties( layerProperties )
346  , mTypeNameUTF8Len( 0 )
347  , mWkbType( QgsWkbTypes::Unknown )
348  , mGeometryAttributeUTF8Len( 0 )
349  , mFields( fields )
350  , mIsException( false )
351  , mTruncatedResponse( false )
352  , mParseDepth( 0 )
353  , mFeatureTupleDepth( 0 )
354  , mFeatureCount( 0 )
355  , mCurrentWKB( nullptr, 0 )
356  , mBoundedByNullFound( false )
357  , mDimension( 0 )
358  , mCoorMode( Coordinate )
359  , mEpsg( 0 )
360  , mAxisOrientationLogic( axisOrientationLogic )
361  , mInvertAxisOrientationRequest( invertAxisOrientation )
362  , mInvertAxisOrientation( invertAxisOrientation )
363  , mNumberReturned( -1 )
364  , mNumberMatched( -1 )
365  , mFoundUnhandledGeometryElement( false )
366 {
367  mThematicAttributes.clear();
368  for ( int i = 0; i < fields.size(); i++ )
369  {
370  QMap< QString, QPair<QString, QString> >::const_iterator att_it = mapFieldNameToSrcLayerNameFieldName.constFind( fields.at( i ).name() );
371  if ( att_it != mapFieldNameToSrcLayerNameFieldName.constEnd() )
372  {
373  if ( mLayerProperties.size() == 1 )
374  mThematicAttributes.insert( att_it.value().second, qMakePair( i, fields.at( i ) ) );
375  else
376  mThematicAttributes.insert( stripNS( att_it.value().first ) + "|" + att_it.value().second, qMakePair( i, fields.at( i ) ) );
377  }
378  }
379  bool alreadyFoundGeometry = false;
380  for ( int i = 0; i < mLayerProperties.size(); i++ )
381  {
382  // We only support one geometry field per feature
383  if ( !mLayerProperties[i].mGeometryAttribute.isEmpty() )
384  {
385  if ( alreadyFoundGeometry )
386  {
387  QgsDebugMsg( QStringLiteral( "Will ignore geometry field %1 from typename %2" ).
388  arg( mLayerProperties[i].mGeometryAttribute, mLayerProperties[i].mName ) );
389  mLayerProperties[i].mGeometryAttribute.clear();
390  }
391  alreadyFoundGeometry = true;
392  }
393  mMapTypeNameToProperties.insert( stripNS( mLayerProperties[i].mName ), mLayerProperties[i] );
394  }
395 
396  if ( mLayerProperties.size() == 1 )
397  {
398  mTypeName = mLayerProperties[0].mName;
399  mGeometryAttribute = mLayerProperties[0].mGeometryAttribute;
400  mGeometryAttributeBA = mGeometryAttribute.toUtf8();
401  mGeometryAttributePtr = mGeometryAttributeBA.constData();
402  mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
403  int index = mTypeName.indexOf( ':' );
404  if ( index != -1 && index < mTypeName.length() )
405  {
406  mTypeName = mTypeName.mid( index + 1 );
407  }
408  mTypeNameBA = mTypeName.toUtf8();
409  mTypeNamePtr = mTypeNameBA.constData();
410  mTypeNameUTF8Len = strlen( mTypeNamePtr );
411  }
412 
413  mEndian = QgsApplication::endian();
414 
415  mParser = XML_ParserCreateNS( nullptr, NS_SEPARATOR );
416  XML_SetUserData( mParser, this );
417  XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
418  XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
419 }
420 
421 
423 {
424  XML_ParserFree( mParser );
425 
426  // Normally a sane user of this class should have consumed everything...
427  const auto constMFeatureList = mFeatureList;
428  for ( QgsGmlFeaturePtrGmlIdPair featPair : constMFeatureList )
429  {
430  delete featPair.first;
431  }
432 
433  delete mCurrentFeature;
434 }
435 
436 bool QgsGmlStreamingParser::processData( const QByteArray &data, bool atEnd )
437 {
438  QString errorMsg;
439  if ( !processData( data, atEnd, errorMsg ) )
440  {
441  QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
442  return false;
443  }
444  return true;
445 }
446 
447 bool QgsGmlStreamingParser::processData( const QByteArray &data, bool atEnd, QString &errorMsg )
448 {
449  if ( XML_Parse( mParser, data.data(), data.size(), atEnd ) == 0 )
450  {
451  XML_Error errorCode = XML_GetErrorCode( mParser );
452  errorMsg = QObject::tr( "Error: %1 on line %2, column %3" )
453  .arg( XML_ErrorString( errorCode ) )
454  .arg( XML_GetCurrentLineNumber( mParser ) )
455  .arg( XML_GetCurrentColumnNumber( mParser ) );
456 
457  return false;
458  }
459 
460  return true;
461 }
462 
463 QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> QgsGmlStreamingParser::getAndStealReadyFeatures()
464 {
465  QVector<QgsGmlFeaturePtrGmlIdPair> ret = mFeatureList;
466  mFeatureList.clear();
467  return ret;
468 }
469 
470 #define LOCALNAME_EQUALS(string_constant) \
471  ( localNameLen == static_cast<int>(strlen( string_constant )) && memcmp(pszLocalName, string_constant, localNameLen) == 0 )
472 
473 void QgsGmlStreamingParser::startElement( const XML_Char *el, const XML_Char **attr )
474 {
475  const int elLen = static_cast<int>( strlen( el ) );
476  const char *pszSep = strchr( el, NS_SEPARATOR );
477  const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
478  const int nsLen = ( pszSep ) ? ( int )( pszSep - el ) : 0;
479  const int localNameLen = ( pszSep ) ? ( int )( elLen - nsLen ) - 1 : elLen;
480  ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
481  int elDimension = 0;
482 
483  // Figure out if the GML namespace is GML_NAMESPACE or GML32_NAMESPACE
484  if ( !mGMLNameSpaceURIPtr && pszSep )
485  {
486  if ( nsLen == static_cast<int>( strlen( GML_NAMESPACE ) ) && memcmp( el, GML_NAMESPACE, nsLen ) == 0 )
487  {
488  mGMLNameSpaceURI = GML_NAMESPACE;
489  mGMLNameSpaceURIPtr = GML_NAMESPACE;
490  }
491  else if ( nsLen == static_cast<int>( strlen( GML32_NAMESPACE ) ) && memcmp( el, GML32_NAMESPACE, nsLen ) == 0 )
492  {
493  mGMLNameSpaceURI = GML32_NAMESPACE;
494  mGMLNameSpaceURIPtr = GML32_NAMESPACE;
495  }
496  }
497 
498  const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
499  bool isGeom = false;
500 
501  if ( parseMode == Geometry || parseMode == Coordinate || parseMode == PosList ||
502  parseMode == MultiPoint || parseMode == MultiLine || parseMode == MultiPolygon )
503  {
504  mGeometryString.append( "<", 1 );
505  mGeometryString.append( pszLocalName, localNameLen );
506  mGeometryString.append( " ", 1 );
507  for ( const XML_Char **attrIter = attr; attrIter && *attrIter; attrIter += 2 )
508  {
509  mGeometryString.append( attrIter[0] );
510  mGeometryString.append( "=\"", 2 );
511  mGeometryString.append( attrIter[1] );
512  mGeometryString.append( "\" ", 2 );
513 
514  }
515  mGeometryString.append( ">", 1 );
516  }
517 
518  if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
519  {
520  mParseModeStack.push( Coordinate );
521  mCoorMode = QgsGmlStreamingParser::Coordinate;
522  mStringCash.clear();
523  mCoordinateSeparator = readAttribute( QStringLiteral( "cs" ), attr );
524  if ( mCoordinateSeparator.isEmpty() )
525  {
526  mCoordinateSeparator = ',';
527  }
528  mTupleSeparator = readAttribute( QStringLiteral( "ts" ), attr );
529  if ( mTupleSeparator.isEmpty() )
530  {
531  mTupleSeparator = ' ';
532  }
533  }
534  else if ( isGMLNS &&
535  ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
536  {
537  mParseModeStack.push( QgsGmlStreamingParser::PosList );
538  mCoorMode = QgsGmlStreamingParser::PosList;
539  mStringCash.clear();
540  if ( elDimension == 0 )
541  {
542  QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
543  bool ok;
544  int dimension = srsDimension.toInt( &ok );
545  if ( ok )
546  {
547  elDimension = dimension;
548  }
549  }
550  }
551  else if ( ( parseMode == Feature || parseMode == FeatureTuple ) &&
552  mCurrentFeature &&
553  localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
554  memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
555  {
556  mParseModeStack.push( QgsGmlStreamingParser::Geometry );
557  mFoundUnhandledGeometryElement = false;
558  mGeometryString.clear();
559  }
560  //else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
561  else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
562  {
563  mParseModeStack.push( QgsGmlStreamingParser::BoundingBox );
564  mCurrentExtent = QgsRectangle();
565  mBoundedByNullFound = false;
566  }
567  else if ( parseMode == BoundingBox &&
568  isGMLNS && LOCALNAME_EQUALS( "null" ) )
569  {
570  mParseModeStack.push( QgsGmlStreamingParser::Null );
571  mBoundedByNullFound = true;
572  }
573  else if ( parseMode == BoundingBox &&
574  isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
575  {
576  isGeom = true;
577  mParseModeStack.push( QgsGmlStreamingParser::Envelope );
578  }
579  else if ( parseMode == Envelope &&
580  isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
581  {
582  mParseModeStack.push( QgsGmlStreamingParser::LowerCorner );
583  mStringCash.clear();
584  }
585  else if ( parseMode == Envelope &&
586  isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
587  {
588  mParseModeStack.push( QgsGmlStreamingParser::UpperCorner );
589  mStringCash.clear();
590  }
591  else if ( parseMode == None && !mTypeNamePtr &&
592  LOCALNAME_EQUALS( "Tuple" ) )
593  {
594  Q_ASSERT( !mCurrentFeature );
595  mCurrentFeature = new QgsFeature( mFeatureCount );
596  mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
597  QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
598  mCurrentFeature->setAttributes( attributes );
599  mParseModeStack.push( QgsGmlStreamingParser::Tuple );
600  mCurrentFeatureId.clear();
601  }
602  else if ( parseMode == Tuple )
603  {
604  QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) );
605  QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename );
606  if ( iter != mMapTypeNameToProperties.constEnd() )
607  {
608  mFeatureTupleDepth = mParseDepth;
609  mCurrentTypename = currentTypename;
610  mGeometryAttribute.clear();
611  if ( mCurrentWKB.size() == 0 )
612  {
613  mGeometryAttribute = iter.value().mGeometryAttribute;
614  }
615  mGeometryAttributeBA = mGeometryAttribute.toUtf8();
616  mGeometryAttributePtr = mGeometryAttributeBA.constData();
617  mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
618  mParseModeStack.push( QgsGmlStreamingParser::FeatureTuple );
619  QString id;
620  if ( mGMLNameSpaceURI.isEmpty() )
621  {
622  id = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
623  if ( !id.isEmpty() )
624  {
625  mGMLNameSpaceURI = GML_NAMESPACE;
626  mGMLNameSpaceURIPtr = GML_NAMESPACE;
627  }
628  else
629  {
630  id = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
631  if ( !id.isEmpty() )
632  {
633  mGMLNameSpaceURI = GML32_NAMESPACE;
634  mGMLNameSpaceURIPtr = GML32_NAMESPACE;
635  }
636  }
637  }
638  else
639  id = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
640  if ( !mCurrentFeatureId.isEmpty() )
641  mCurrentFeatureId += '|';
642  mCurrentFeatureId += id;
643  }
644  }
645  else if ( parseMode == None &&
646  localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
647  memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 )
648  {
649  Q_ASSERT( !mCurrentFeature );
650  mCurrentFeature = new QgsFeature( mFeatureCount );
651  mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
652  QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
653  mCurrentFeature->setAttributes( attributes );
654  mParseModeStack.push( QgsGmlStreamingParser::Feature );
655  mCurrentFeatureId = readAttribute( QStringLiteral( "fid" ), attr );
656  if ( mCurrentFeatureId.isEmpty() )
657  {
658  // Figure out if the GML namespace is GML_NAMESPACE or GML32_NAMESPACE
659  // (should happen only for the first features if there's no gml: element
660  // encountered before
661  if ( mGMLNameSpaceURI.isEmpty() )
662  {
663  mCurrentFeatureId = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
664  if ( !mCurrentFeatureId.isEmpty() )
665  {
666  mGMLNameSpaceURI = GML_NAMESPACE;
667  mGMLNameSpaceURIPtr = GML_NAMESPACE;
668  }
669  else
670  {
671  mCurrentFeatureId = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
672  if ( !mCurrentFeatureId.isEmpty() )
673  {
674  mGMLNameSpaceURI = GML32_NAMESPACE;
675  mGMLNameSpaceURIPtr = GML32_NAMESPACE;
676  }
677  }
678  }
679  else
680  mCurrentFeatureId = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
681  }
682  }
683 
684  else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "Box" ) )
685  {
686  isGeom = true;
687  }
688  else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
689  {
690  isGeom = true;
691  }
692  else if ( isGMLNS && LOCALNAME_EQUALS( "LineString" ) )
693  {
694  isGeom = true;
695  }
696  else if ( isGMLNS &&
697  localNameLen == static_cast<int>( strlen( "Polygon" ) ) && memcmp( pszLocalName, "Polygon", localNameLen ) == 0 )
698  {
699  isGeom = true;
700  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
701  }
702  else if ( isGMLNS && LOCALNAME_EQUALS( "MultiPoint" ) )
703  {
704  isGeom = true;
705  mParseModeStack.push( QgsGmlStreamingParser::MultiPoint );
706  //we need one nested list for intermediate WKB
707  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
708  }
709  else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
710  {
711  isGeom = true;
712  mParseModeStack.push( QgsGmlStreamingParser::MultiLine );
713  //we need one nested list for intermediate WKB
714  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
715  }
716  else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
717  {
718  isGeom = true;
719  mParseModeStack.push( QgsGmlStreamingParser::MultiPolygon );
720  }
721  else if ( parseMode == FeatureTuple )
722  {
723  QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
724  if ( mThematicAttributes.contains( mCurrentTypename + '|' + localName ) )
725  {
726  mParseModeStack.push( QgsGmlStreamingParser::AttributeTuple );
727  mAttributeName = mCurrentTypename + '|' + localName;
728  mStringCash.clear();
729  }
730  }
731  else if ( parseMode == Feature )
732  {
733  QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
734  if ( mThematicAttributes.contains( localName ) )
735  {
736  mParseModeStack.push( QgsGmlStreamingParser::Attribute );
737  mAttributeName = localName;
738  mStringCash.clear();
739  }
740  else
741  {
742  // QGIS server (2.2) is using:
743  // <Attribute value="My description" name="desc"/>
744  if ( localName.compare( QLatin1String( "attribute" ), Qt::CaseInsensitive ) == 0 )
745  {
746  QString name = readAttribute( QStringLiteral( "name" ), attr );
747  if ( mThematicAttributes.contains( name ) )
748  {
749  QString value = readAttribute( QStringLiteral( "value" ), attr );
750  setAttribute( name, value );
751  }
752  }
753  }
754  }
755  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "FeatureCollection" ) )
756  {
757  QString numberReturned = readAttribute( QStringLiteral( "numberReturned" ), attr ); // WFS 2.0
758  if ( numberReturned.isEmpty() )
759  numberReturned = readAttribute( QStringLiteral( "numberOfFeatures" ), attr ); // WFS 1.1
760  bool conversionOk;
761  mNumberReturned = numberReturned.toInt( &conversionOk );
762  if ( !conversionOk )
763  mNumberReturned = -1;
764 
765  QString numberMatched = readAttribute( QStringLiteral( "numberMatched" ), attr ); // WFS 2.0
766  mNumberMatched = numberMatched.toInt( &conversionOk );
767  if ( !conversionOk ) // likely since numberMatched="unknown" is legal
768  mNumberMatched = -1;
769  }
770  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
771  {
772  mIsException = true;
773  mParseModeStack.push( QgsGmlStreamingParser::ExceptionReport );
774  }
775  else if ( mIsException && LOCALNAME_EQUALS( "ExceptionText" ) )
776  {
777  mStringCash.clear();
778  mParseModeStack.push( QgsGmlStreamingParser::ExceptionText );
779  }
780  else if ( mParseDepth == 1 && LOCALNAME_EQUALS( "truncatedResponse" ) )
781  {
782  // e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
783  mTruncatedResponse = true;
784  }
785  else if ( !mGeometryString.empty() &&
786  !LOCALNAME_EQUALS( "exterior" ) &&
787  !LOCALNAME_EQUALS( "interior" ) &&
788  !LOCALNAME_EQUALS( "innerBoundaryIs" ) &&
789  !LOCALNAME_EQUALS( "outerBoundaryIs" ) &&
790  !LOCALNAME_EQUALS( "LinearRing" ) &&
791  !LOCALNAME_EQUALS( "pointMember" ) &&
792  !LOCALNAME_EQUALS( "curveMember" ) &&
793  !LOCALNAME_EQUALS( "lineStringMember" ) &&
794  !LOCALNAME_EQUALS( "polygonMember" ) &&
795  !LOCALNAME_EQUALS( "surfaceMember" ) &&
796  !LOCALNAME_EQUALS( "Curve" ) &&
797  !LOCALNAME_EQUALS( "segments" ) &&
798  !LOCALNAME_EQUALS( "LineStringSegment" ) )
799  {
800  //QgsDebugMsg( "Found unhandled geometry element " + QString::fromUtf8( pszLocalName, localNameLen ) );
801  mFoundUnhandledGeometryElement = true;
802  }
803 
804  if ( !mGeometryString.empty() )
805  isGeom = true;
806 
807  if ( elDimension == 0 && isGeom )
808  {
809  // srsDimension can also be set on the top geometry element
810  // e.g. https://data.linz.govt.nz/services;key=XXXXXXXX/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=data.linz.govt.nz:layer-524
811  QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
812  bool ok;
813  int dimension = srsDimension.toInt( &ok );
814  if ( ok )
815  {
816  elDimension = dimension;
817  }
818  }
819 
820  if ( elDimension != 0 || mDimensionStack.isEmpty() )
821  {
822  mDimensionStack.push( elDimension );
823  }
824  else
825  {
826  mDimensionStack.push( mDimensionStack.back() );
827  }
828 
829  if ( mEpsg == 0 && isGeom )
830  {
831  if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
832  {
833  QgsDebugMsg( QStringLiteral( "error, could not get epsg id" ) );
834  }
835  else
836  {
837  QgsDebugMsg( QStringLiteral( "mEpsg = %1" ).arg( mEpsg ) );
838  }
839  }
840 
841  mParseDepth ++;
842 }
843 
844 void QgsGmlStreamingParser::endElement( const XML_Char *el )
845 {
846  mParseDepth --;
847 
848  const int elLen = static_cast<int>( strlen( el ) );
849  const char *pszSep = strchr( el, NS_SEPARATOR );
850  const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
851  const int nsLen = ( pszSep ) ? ( int )( pszSep - el ) : 0;
852  const int localNameLen = ( pszSep ) ? ( int )( elLen - nsLen ) - 1 : elLen;
853  ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
854 
855  int lastDimension = mDimensionStack.isEmpty() ? 0 : mDimensionStack.pop();
856 
857  const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
858 
859  if ( parseMode == Coordinate && isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
860  {
861  mParseModeStack.pop();
862  }
863  else if ( parseMode == PosList && isGMLNS &&
864  ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
865  {
866  mDimension = lastDimension;
867  mParseModeStack.pop();
868  }
869  else if ( parseMode == AttributeTuple &&
870  mCurrentTypename + '|' + QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
871  {
872  mParseModeStack.pop();
873 
874  setAttribute( mAttributeName, mStringCash );
875  }
876  else if ( parseMode == Attribute && QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
877  {
878  mParseModeStack.pop();
879 
880  setAttribute( mAttributeName, mStringCash );
881  }
882  else if ( parseMode == Geometry &&
883  localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
884  memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
885  {
886  mParseModeStack.pop();
887  if ( mFoundUnhandledGeometryElement )
888  {
889  gdal::ogr_geometry_unique_ptr hGeom( OGR_G_CreateFromGML( mGeometryString.c_str() ) );
890  if ( hGeom )
891  {
892  const int wkbSize = OGR_G_WkbSize( hGeom.get() );
893  unsigned char *pabyBuffer = new unsigned char[ wkbSize ];
894  OGR_G_ExportToIsoWkb( hGeom.get(), wkbNDR, pabyBuffer );
895  QgsGeometry g;
896  g.fromWkb( pabyBuffer, wkbSize );
897  if ( mInvertAxisOrientation )
898  {
899  g.transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
900  }
901  Q_ASSERT( mCurrentFeature );
902  mCurrentFeature->setGeometry( g );
903  }
904  }
905  mGeometryString.clear();
906  }
907  else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
908  {
909  //create bounding box from mStringCash
910  if ( mCurrentExtent.isNull() &&
911  !mBoundedByNullFound &&
912  !createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) )
913  {
914  QgsDebugMsg( QStringLiteral( "creation of bounding box failed" ) );
915  }
916  if ( !mCurrentExtent.isNull() && mLayerExtent.isNull() &&
917  !mCurrentFeature && mFeatureCount == 0 )
918  {
919  mLayerExtent = mCurrentExtent;
920  mCurrentExtent = QgsRectangle();
921  }
922 
923  mParseModeStack.pop();
924  }
925  else if ( parseMode == Null && isGMLNS && LOCALNAME_EQUALS( "null" ) )
926  {
927  mParseModeStack.pop();
928  }
929  else if ( parseMode == Envelope && isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
930  {
931  mParseModeStack.pop();
932  }
933  else if ( parseMode == LowerCorner && isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
934  {
935  QList<QgsPointXY> points;
936  pointsFromPosListString( points, mStringCash, 2 );
937  if ( points.size() == 1 )
938  {
939  mCurrentExtent.setXMinimum( points[0].x() );
940  mCurrentExtent.setYMinimum( points[0].y() );
941  }
942  mParseModeStack.pop();
943  }
944  else if ( parseMode == UpperCorner && isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
945  {
946  QList<QgsPointXY> points;
947  pointsFromPosListString( points, mStringCash, 2 );
948  if ( points.size() == 1 )
949  {
950  mCurrentExtent.setXMaximum( points[0].x() );
951  mCurrentExtent.setYMaximum( points[0].y() );
952  }
953  mParseModeStack.pop();
954  }
955  else if ( parseMode == FeatureTuple && mParseDepth == mFeatureTupleDepth )
956  {
957  mParseModeStack.pop();
958  mFeatureTupleDepth = 0;
959  }
960  else if ( ( parseMode == Tuple && !mTypeNamePtr &&
961  LOCALNAME_EQUALS( "Tuple" ) ) ||
962  ( parseMode == Feature &&
963  localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
964  memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 ) )
965  {
966  Q_ASSERT( mCurrentFeature );
967  if ( !mCurrentFeature->hasGeometry() )
968  {
969  if ( mCurrentWKB.size() > 0 )
970  {
971  QgsGeometry g;
972  g.fromWkb( mCurrentWKB, mCurrentWKB.size() );
973  mCurrentFeature->setGeometry( g );
974  mCurrentWKB = QgsWkbPtr( nullptr, 0 );
975  }
976  else if ( !mCurrentExtent.isEmpty() )
977  {
978  mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
979  }
980  }
981  mCurrentFeature->setValid( true );
982 
983  mFeatureList.push_back( QgsGmlFeaturePtrGmlIdPair( mCurrentFeature, mCurrentFeatureId ) );
984 
985  mCurrentFeature = nullptr;
986  ++mFeatureCount;
987  mParseModeStack.pop();
988  }
989  else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
990  {
991  QList<QgsPointXY> pointList;
992  if ( pointsFromString( pointList, mStringCash ) != 0 )
993  {
994  //error
995  }
996 
997  if ( pointList.isEmpty() )
998  return; // error
999 
1000  if ( parseMode == QgsGmlStreamingParser::Geometry )
1001  {
1002  //directly add WKB point to the feature
1003  if ( getPointWKB( mCurrentWKB, *( pointList.constBegin() ) ) != 0 )
1004  {
1005  //error
1006  }
1007 
1008  if ( mWkbType != QgsWkbTypes::MultiPoint ) //keep multitype in case of geometry type mix
1009  {
1010  mWkbType = QgsWkbTypes::Point;
1011  }
1012  }
1013  else //multipoint, add WKB as fragment
1014  {
1015  QgsWkbPtr wkbPtr( nullptr, 0 );
1016  if ( getPointWKB( wkbPtr, *( pointList.constBegin() ) ) != 0 )
1017  {
1018  //error
1019  }
1020  if ( !mCurrentWKBFragments.isEmpty() )
1021  {
1022  mCurrentWKBFragments.last().push_back( wkbPtr );
1023  }
1024  else
1025  {
1026  QgsDebugMsg( QStringLiteral( "No wkb fragments" ) );
1027  delete [] wkbPtr;
1028  }
1029  }
1030  }
1031  else if ( isGMLNS && ( LOCALNAME_EQUALS( "LineString" ) || LOCALNAME_EQUALS( "LineStringSegment" ) ) )
1032  {
1033  //add WKB point to the feature
1034 
1035  QList<QgsPointXY> pointList;
1036  if ( pointsFromString( pointList, mStringCash ) != 0 )
1037  {
1038  //error
1039  }
1040  if ( parseMode == QgsGmlStreamingParser::Geometry )
1041  {
1042  if ( getLineWKB( mCurrentWKB, pointList ) != 0 )
1043  {
1044  //error
1045  }
1046 
1047  if ( mWkbType != QgsWkbTypes::MultiLineString )//keep multitype in case of geometry type mix
1048  {
1049  mWkbType = QgsWkbTypes::LineString;
1050  }
1051  }
1052  else //multiline, add WKB as fragment
1053  {
1054  QgsWkbPtr wkbPtr( nullptr, 0 );
1055  if ( getLineWKB( wkbPtr, pointList ) != 0 )
1056  {
1057  //error
1058  }
1059  if ( !mCurrentWKBFragments.isEmpty() )
1060  {
1061  mCurrentWKBFragments.last().push_back( wkbPtr );
1062  }
1063  else
1064  {
1065  QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1066  delete [] wkbPtr;
1067  }
1068  }
1069  }
1070  else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) &&
1071  isGMLNS && LOCALNAME_EQUALS( "LinearRing" ) )
1072  {
1073  QList<QgsPointXY> pointList;
1074  if ( pointsFromString( pointList, mStringCash ) != 0 )
1075  {
1076  //error
1077  }
1078 
1079  QgsWkbPtr wkbPtr( nullptr, 0 );
1080  if ( getRingWKB( wkbPtr, pointList ) != 0 )
1081  {
1082  //error
1083  }
1084 
1085  if ( !mCurrentWKBFragments.isEmpty() )
1086  {
1087  mCurrentWKBFragments.last().push_back( wkbPtr );
1088  }
1089  else
1090  {
1091  delete[] wkbPtr;
1092  QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1093  }
1094  }
1095  else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) && isGMLNS &&
1096  LOCALNAME_EQUALS( "Polygon" ) )
1097  {
1098  if ( mWkbType != QgsWkbTypes::MultiPolygon )//keep multitype in case of geometry type mix
1099  {
1100  mWkbType = QgsWkbTypes::Polygon;
1101  }
1102 
1103  if ( parseMode == Geometry )
1104  {
1105  createPolygonFromFragments();
1106  }
1107  }
1108  else if ( parseMode == MultiPoint && isGMLNS &&
1109  LOCALNAME_EQUALS( "MultiPoint" ) )
1110  {
1111  mWkbType = QgsWkbTypes::MultiPoint;
1112  mParseModeStack.pop();
1113  createMultiPointFromFragments();
1114  }
1115  else if ( parseMode == MultiLine && isGMLNS &&
1116  ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
1117  {
1118  mWkbType = QgsWkbTypes::MultiLineString;
1119  mParseModeStack.pop();
1120  createMultiLineFromFragments();
1121  }
1122  else if ( parseMode == MultiPolygon && isGMLNS &&
1123  ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
1124  {
1125  mWkbType = QgsWkbTypes::MultiPolygon;
1126  mParseModeStack.pop();
1127  createMultiPolygonFromFragments();
1128  }
1129  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
1130  {
1131  mParseModeStack.pop();
1132  }
1133  else if ( parseMode == ExceptionText && LOCALNAME_EQUALS( "ExceptionText" ) )
1134  {
1135  mExceptionText = mStringCash;
1136  mParseModeStack.pop();
1137  }
1138 
1139  if ( !mGeometryString.empty() )
1140  {
1141  mGeometryString.append( "</", 2 );
1142  mGeometryString.append( pszLocalName, localNameLen );
1143  mGeometryString.append( ">", 1 );
1144  }
1145 
1146 }
1147 
1148 void QgsGmlStreamingParser::characters( const XML_Char *chars, int len )
1149 {
1150  //save chars in mStringCash attribute mode or coordinate mode
1151  if ( mParseModeStack.isEmpty() )
1152  {
1153  return;
1154  }
1155 
1156  if ( !mGeometryString.empty() )
1157  {
1158  mGeometryString.append( chars, len );
1159  }
1160 
1161  QgsGmlStreamingParser::ParseMode parseMode = mParseModeStack.top();
1162  if ( parseMode == QgsGmlStreamingParser::Attribute ||
1163  parseMode == QgsGmlStreamingParser::AttributeTuple ||
1164  parseMode == QgsGmlStreamingParser::Coordinate ||
1165  parseMode == QgsGmlStreamingParser::PosList ||
1166  parseMode == QgsGmlStreamingParser::LowerCorner ||
1167  parseMode == QgsGmlStreamingParser::UpperCorner ||
1168  parseMode == QgsGmlStreamingParser::ExceptionText )
1169  {
1170  mStringCash.append( QString::fromUtf8( chars, len ) );
1171  }
1172 }
1173 
1174 void QgsGmlStreamingParser::setAttribute( const QString &name, const QString &value )
1175 {
1176  //find index with attribute name
1177  QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.constFind( name );
1178  bool conversionOk = true;
1179  if ( att_it != mThematicAttributes.constEnd() )
1180  {
1181  QVariant var;
1182  switch ( att_it.value().second.type() )
1183  {
1184  case QVariant::Double:
1185  var = QVariant( value.toDouble( &conversionOk ) );
1186  break;
1187  case QVariant::Int:
1188  var = QVariant( value.toInt( &conversionOk ) );
1189  break;
1190  case QVariant::LongLong:
1191  var = QVariant( value.toLongLong( &conversionOk ) );
1192  break;
1193  case QVariant::DateTime:
1194  var = QVariant( QDateTime::fromString( value, Qt::ISODate ) );
1195  break;
1196  default: //string type is default
1197  var = QVariant( value );
1198  break;
1199  }
1200  if ( ! conversionOk ) // Assume is NULL
1201  {
1202  var = QVariant();
1203  }
1204  Q_ASSERT( mCurrentFeature );
1205  mCurrentFeature->setAttribute( att_it.value().first, var );
1206  }
1207 }
1208 
1209 int QgsGmlStreamingParser::readEpsgFromAttribute( int &epsgNr, const XML_Char **attr )
1210 {
1211  int i = 0;
1212  while ( attr[i] )
1213  {
1214  if ( strcmp( attr[i], "srsName" ) == 0 )
1215  {
1216  QString epsgString( attr[i + 1] );
1217  QString epsgNrString;
1218  bool bIsUrn = false;
1219  if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) ) //e.g. geoserver: "http://www.opengis.net/gml/srs/epsg.xml#4326"
1220  {
1221  epsgNrString = epsgString.section( '#', 1, 1 );
1222  }
1223  // WFS >= 1.1
1224  else if ( epsgString.startsWith( QLatin1String( "urn:ogc:def:crs:EPSG:" ) ) ||
1225  epsgString.startsWith( QLatin1String( "urn:x-ogc:def:crs:EPSG:" ) ) )
1226  {
1227  bIsUrn = true;
1228  epsgNrString = epsgString.split( ':' ).last();
1229  }
1230  else if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/def/crs/EPSG/" ) ) ) //e.g. geoserver: "http://www.opengis.net/def/crs/EPSG/4326"
1231  {
1232  bIsUrn = true;
1233  epsgNrString = epsgString.split( '/' ).last();
1234  }
1235  else //e.g. umn mapserver: "EPSG:4326">
1236  {
1237  epsgNrString = epsgString.section( ':', 1, 1 );
1238  }
1239  bool conversionOk;
1240  int eNr = epsgNrString.toInt( &conversionOk );
1241  if ( !conversionOk )
1242  {
1243  return 1;
1244  }
1245  epsgNr = eNr;
1246  mSrsName = epsgString;
1247 
1248  QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( epsgNr ) );
1249  if ( crs.isValid() )
1250  {
1251  if ( ( ( mAxisOrientationLogic == Honour_EPSG_if_urn && bIsUrn ) ||
1252  mAxisOrientationLogic == Honour_EPSG ) && crs.hasAxisInverted() )
1253  {
1254  mInvertAxisOrientation = !mInvertAxisOrientationRequest;
1255  }
1256  }
1257 
1258  return 0;
1259  }
1260  ++i;
1261  }
1262  return 2;
1263 }
1264 
1265 QString QgsGmlStreamingParser::readAttribute( const QString &attributeName, const XML_Char **attr ) const
1266 {
1267  int i = 0;
1268  while ( attr[i] )
1269  {
1270  if ( attributeName.compare( attr[i] ) == 0 )
1271  {
1272  return QString::fromUtf8( attr[i + 1] );
1273  }
1274  i += 2;
1275  }
1276  return QString();
1277 }
1278 
1279 bool QgsGmlStreamingParser::createBBoxFromCoordinateString( QgsRectangle &r, const QString &coordString ) const
1280 {
1281  QList<QgsPointXY> points;
1282  if ( pointsFromCoordinateString( points, coordString ) != 0 )
1283  {
1284  return false;
1285  }
1286 
1287  if ( points.size() < 2 )
1288  {
1289  return false;
1290  }
1291 
1292  r.set( points[0], points[1] );
1293 
1294  return true;
1295 }
1296 
1297 int QgsGmlStreamingParser::pointsFromCoordinateString( QList<QgsPointXY> &points, const QString &coordString ) const
1298 {
1299  //tuples are separated by space, x/y by ','
1300  QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
1301  QStringList tuples_coordinates;
1302  double x, y;
1303  bool conversionSuccess;
1304 
1305  QStringList::const_iterator tupleIterator;
1306  for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
1307  {
1308  tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
1309  if ( tuples_coordinates.size() < 2 )
1310  {
1311  continue;
1312  }
1313  x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
1314  if ( !conversionSuccess )
1315  {
1316  continue;
1317  }
1318  y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
1319  if ( !conversionSuccess )
1320  {
1321  continue;
1322  }
1323  points.push_back( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1324  }
1325  return 0;
1326 }
1327 
1328 int QgsGmlStreamingParser::pointsFromPosListString( QList<QgsPointXY> &points, const QString &coordString, int dimension ) const
1329 {
1330  // coordinates separated by spaces
1331  QStringList coordinates = coordString.split( ' ', QString::SkipEmptyParts );
1332 
1333  if ( coordinates.size() % dimension != 0 )
1334  {
1335  QgsDebugMsg( QStringLiteral( "Wrong number of coordinates" ) );
1336  }
1337 
1338  int ncoor = coordinates.size() / dimension;
1339  for ( int i = 0; i < ncoor; i++ )
1340  {
1341  bool conversionSuccess;
1342  double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
1343  if ( !conversionSuccess )
1344  {
1345  continue;
1346  }
1347  double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
1348  if ( !conversionSuccess )
1349  {
1350  continue;
1351  }
1352  points.append( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1353  }
1354  return 0;
1355 }
1356 
1357 int QgsGmlStreamingParser::pointsFromString( QList<QgsPointXY> &points, const QString &coordString ) const
1358 {
1359  if ( mCoorMode == QgsGmlStreamingParser::Coordinate )
1360  {
1361  return pointsFromCoordinateString( points, coordString );
1362  }
1363  else if ( mCoorMode == QgsGmlStreamingParser::PosList )
1364  {
1365  return pointsFromPosListString( points, coordString, mDimension ? mDimension : 2 );
1366  }
1367  return 1;
1368 }
1369 
1370 int QgsGmlStreamingParser::getPointWKB( QgsWkbPtr &wkbPtr, const QgsPointXY &point ) const
1371 {
1372  int wkbSize = 1 + sizeof( int ) + 2 * sizeof( double );
1373  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1374 
1375  QgsWkbPtr fillPtr( wkbPtr );
1376  fillPtr << mEndian << QgsWkbTypes::Point << point.x() << point.y();
1377 
1378  return 0;
1379 }
1380 
1381 int QgsGmlStreamingParser::getLineWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &lineCoordinates ) const
1382 {
1383  int wkbSize = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
1384  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1385 
1386  QgsWkbPtr fillPtr( wkbPtr );
1387 
1388  fillPtr << mEndian << QgsWkbTypes::LineString << lineCoordinates.size();
1389 
1390  QList<QgsPointXY>::const_iterator iter;
1391  for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
1392  {
1393  fillPtr << iter->x() << iter->y();
1394  }
1395 
1396  return 0;
1397 }
1398 
1399 int QgsGmlStreamingParser::getRingWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &ringCoordinates ) const
1400 {
1401  int wkbSize = sizeof( int ) + ringCoordinates.size() * 2 * sizeof( double );
1402  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1403 
1404  QgsWkbPtr fillPtr( wkbPtr );
1405 
1406  fillPtr << ringCoordinates.size();
1407 
1408  QList<QgsPointXY>::const_iterator iter;
1409  for ( iter = ringCoordinates.constBegin(); iter != ringCoordinates.constEnd(); ++iter )
1410  {
1411  fillPtr << iter->x() << iter->y();
1412  }
1413 
1414  return 0;
1415 }
1416 
1417 int QgsGmlStreamingParser::createMultiLineFromFragments()
1418 {
1419  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1420  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1421 
1422  QgsWkbPtr wkbPtr( mCurrentWKB );
1423 
1424  wkbPtr << mEndian << QgsWkbTypes::MultiLineString << mCurrentWKBFragments.constBegin()->size();
1425 
1426  //copy (and delete) all the wkb fragments
1427  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1428  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1429  {
1430  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1431  wkbPtr += wkbIt->size();
1432  delete[] *wkbIt;
1433  }
1434 
1435  mCurrentWKBFragments.clear();
1436  mWkbType = QgsWkbTypes::MultiLineString;
1437  return 0;
1438 }
1439 
1440 int QgsGmlStreamingParser::createMultiPointFromFragments()
1441 {
1442  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1443  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1444 
1445  QgsWkbPtr wkbPtr( mCurrentWKB );
1446  wkbPtr << mEndian << QgsWkbTypes::MultiPoint << mCurrentWKBFragments.constBegin()->size();
1447 
1448  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1449  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1450  {
1451  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1452  wkbPtr += wkbIt->size();
1453  delete[] *wkbIt;
1454  }
1455 
1456  mCurrentWKBFragments.clear();
1457  mWkbType = QgsWkbTypes::MultiPoint;
1458  return 0;
1459 }
1460 
1461 
1462 int QgsGmlStreamingParser::createPolygonFromFragments()
1463 {
1464  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1465  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1466 
1467  QgsWkbPtr wkbPtr( mCurrentWKB );
1468  wkbPtr << mEndian << QgsWkbTypes::Polygon << mCurrentWKBFragments.constBegin()->size();
1469 
1470  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1471  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1472  {
1473  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1474  wkbPtr += wkbIt->size();
1475  delete[] *wkbIt;
1476  }
1477 
1478  mCurrentWKBFragments.clear();
1479  mWkbType = QgsWkbTypes::Polygon;
1480  return 0;
1481 }
1482 
1483 int QgsGmlStreamingParser::createMultiPolygonFromFragments()
1484 {
1485  int size = 0;
1486  size += 1 + 2 * sizeof( int );
1487  size += totalWKBFragmentSize();
1488  size += mCurrentWKBFragments.size() * ( 1 + 2 * sizeof( int ) ); //fragments are just the rings
1489 
1490  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1491 
1492  QgsWkbPtr wkbPtr( mCurrentWKB );
1493  wkbPtr << ( char ) mEndian << QgsWkbTypes::MultiPolygon << mCurrentWKBFragments.size();
1494 
1495  //have outer and inner iterators
1496  QList< QList<QgsWkbPtr> >::const_iterator outerWkbIt = mCurrentWKBFragments.constBegin();
1497 
1498  for ( ; outerWkbIt != mCurrentWKBFragments.constEnd(); ++outerWkbIt )
1499  {
1500  //new polygon
1501  wkbPtr << ( char ) mEndian << QgsWkbTypes::Polygon << outerWkbIt->size();
1502 
1503  QList<QgsWkbPtr>::const_iterator innerWkbIt = outerWkbIt->constBegin();
1504  for ( ; innerWkbIt != outerWkbIt->constEnd(); ++innerWkbIt )
1505  {
1506  memcpy( wkbPtr, *innerWkbIt, innerWkbIt->size() );
1507  wkbPtr += innerWkbIt->size();
1508  delete[] *innerWkbIt;
1509  }
1510  }
1511 
1512  mCurrentWKBFragments.clear();
1513  mWkbType = QgsWkbTypes::MultiPolygon;
1514  return 0;
1515 }
1516 
1517 int QgsGmlStreamingParser::totalWKBFragmentSize() const
1518 {
1519  int result = 0;
1520  const auto constMCurrentWKBFragments = mCurrentWKBFragments;
1521  for ( const QList<QgsWkbPtr> &list : constMCurrentWKBFragments )
1522  {
1523  const auto constList = list;
1524  for ( const QgsWkbPtr &i : constList )
1525  {
1526  result += i.size();
1527  }
1528  }
1529  return result;
1530 }
QgsGml::totalStepsUpdate
void totalStepsUpdate(int totalSteps)
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsGeometry::transform
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Definition: qgsgeometry.cpp:2836
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:71
QgsGmlStreamingParser::Honour_EPSG
@ Honour_EPSG
Honour EPSG axis order.
Definition: qgsgml.h:76
QgsRectangle::combineExtentWith
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
qgsrectangle.h
QgsWkbTypes::MultiPolygon
@ MultiPolygon
Definition: qgswkbtypes.h:77
QgsGml::dataReadProgress
void dataReadProgress(int progress)
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
qgswkbptr.h
qgsauthmanager.h
QgsGmlStreamingParser::processData
bool processData(const QByteArray &data, bool atEnd, QString &errorMsg)
Process a new chunk of data.
Definition: qgsgml.cpp:447
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:200
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:72
QgsFields
Definition: qgsfields.h:44
QgsGeometry::fromWkb
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
Definition: qgsgeometry.cpp:332
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
QgsFeature::setValid
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:188
QgsGmlStreamingParser::Honour_EPSG_if_urn
@ Honour_EPSG_if_urn
Honour EPSG axis order only if srsName is of the form urn:ogc:def:crs:EPSG: *.
Definition: qgsgml.h:74
qgsogrutils.h
QgsGml::QgsGml
QgsGml(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields)
Definition: qgsgml.cpp:43
QgsCoordinateReferenceSystem::hasAxisInverted
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Definition: qgscoordinatereferencesystem.cpp:808
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsWkbPtr::size
int size() const
size
Definition: qgswkbptr.h:107
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsField::name
QString name
Definition: qgsfield.h:59
QgsRectangle
Definition: qgsrectangle.h:41
QgsSetRequestInitiatorClass
#define QgsSetRequestInitiatorClass(request, _class)
Definition: qgsnetworkaccessmanager.h:41
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:229
QgsApplication::authManager
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
Definition: qgsapplication.cpp:1263
QgsGml::dataProgressAndSteps
void dataProgressAndSteps(int progress, int totalSteps)
Also emit signal with progress and totalSteps together (this is better for the status message)
qgsapplication.h
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
NS_SEPARATOR
const char NS_SEPARATOR
Definition: qgsgmlschema.cpp:33
QgsWkbTypes::MultiLineString
@ MultiLineString
Definition: qgswkbtypes.h:76
QgsGmlStreamingParser::~QgsGmlStreamingParser
~QgsGmlStreamingParser()
Definition: qgsgml.cpp:422
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:70
QgsFeature::setGeometry
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:137
QgsGml::getFeatures
int getFeatures(const QString &uri, QgsWkbTypes::Type *wkbType, QgsRectangle *extent=nullptr, const QString &userName=QString(), const QString &password=QString(), const QString &authcfg=QString())
Does the Http GET request to the wfs server Supports only UTF-8, UTF-16, ISO-8859-1,...
Definition: qgsgml.cpp:58
QgsFeature::setFields
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:162
qgsnetworkaccessmanager.h
QgsGmlStreamingParser::getEPSGCode
int getEPSGCode() const
Returns the EPSG code, or 0 if unknown.
Definition: qgsgml.h:119
QgsFields::size
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:902
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1230
gdal::ogr_geometry_unique_ptr
std::unique_ptr< std::remove_pointer< OGRGeometryH >::type, OGRGeometryDeleter > ogr_geometry_unique_ptr
Scoped OGR geometry.
Definition: qgsogrutils.h:119
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:109
QgsWkbPtr
Definition: qgswkbptr.h:42
QgsNetworkAccessManager::instance
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Definition: qgsnetworkaccessmanager.cpp:121
QgsCoordinateReferenceSystem
Definition: qgscoordinatereferencesystem.h:206
QgsPointXY
Definition: qgspointxy.h:43
QgsFeature::setAttribute
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
Definition: qgsfeature.cpp:211
qgsgml.h
qgsgeometry.h
QgsGmlStreamingParser::AxisOrientationLogic
AxisOrientationLogic
Axis orientation logic.
Definition: qgsgml.h:71
QgsGeometry
Definition: qgsgeometry.h:122
QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair
QPair< QgsFeature *, QString > QgsGmlFeaturePtrGmlIdPair
Definition: qgsgml.h:52
QgsGmlStreamingParser::getAndStealReadyFeatures
QVector< QgsGmlFeaturePtrGmlIdPair > getAndStealReadyFeatures()
Returns the list of features that have been completely parsed.
Definition: qgsgml.cpp:463
QgsFeature::hasGeometry
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
QgsRectangle::set
void set(const QgsPointXY &p1, const QgsPointXY &p2)
Sets the rectangle from two QgsPoints.
Definition: qgsrectangle.h:105
QgsGmlStreamingParser::numberMatched
int numberMatched() const
Returns WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found.
Definition: qgsgml.h:131
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:73
QgsGmlStreamingParser::QgsGmlStreamingParser
QgsGmlStreamingParser(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields, AxisOrientationLogic axisOrientationLogic=Honour_EPSG_if_urn, bool invertAxisOrientation=false)
Constructor.
Definition: qgsgml.cpp:275
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:75
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsGmlStreamingParser::numberReturned
int numberReturned() const
Returns WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found.
Definition: qgsgml.h:134
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
Qgis::Critical
@ Critical
Definition: qgis.h:105
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:962
QgsAttributes
Definition: qgsattributes.h:57
QgsFeature
Definition: qgsfeature.h:55
QgsWkbTypes
Handles storage of information regarding WKB types and their properties.
Definition: qgswkbtypes.h:40
QgsRectangle::setMinimal
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:151
qgslogger.h
GML32_NAMESPACE
#define GML32_NAMESPACE
Definition: qgsogcutils.cpp:42
QgsFeature::setAttributes
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:127
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsRectangle::isEmpty
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
qgscoordinatereferencesystem.h
LOCALNAME_EQUALS
#define LOCALNAME_EQUALS(string_constant)
Definition: qgsgml.cpp:470
QgsGmlStreamingParser::wkbType
QgsWkbTypes::Type wkbType() const
Returns the geometry type.
Definition: qgsgml.h:128
QgsRectangle::isNull
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
QgsGml::crs
QgsCoordinateReferenceSystem crs() const
Returns features spatial reference system.
Definition: qgsgml.cpp:261
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
GML_NAMESPACE
#define GML_NAMESPACE
Definition: qgsgmlschema.cpp:34
qgsmessagelog.h