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