QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  const size_t nAttrLen = strlen( attrIter[0] );
510  const size_t GML32_NAMESPACE_LEN = strlen( GML32_NAMESPACE );
511  const size_t GML_NAMESPACE_LEN = strlen( GML_NAMESPACE );
512  if ( nAttrLen > GML32_NAMESPACE_LEN &&
513  attrIter[0][GML32_NAMESPACE_LEN] == '?' &&
514  memcmp( attrIter[0], GML32_NAMESPACE, GML32_NAMESPACE_LEN ) == 0 )
515  {
516  mGeometryString.append( "gml:" );
517  mGeometryString.append( attrIter[0] + GML32_NAMESPACE_LEN + 1 );
518  }
519  else if ( nAttrLen > GML_NAMESPACE_LEN &&
520  attrIter[0][GML_NAMESPACE_LEN] == '?' &&
521  memcmp( attrIter[0], GML_NAMESPACE, GML_NAMESPACE_LEN ) == 0 )
522  {
523  mGeometryString.append( "gml:" );
524  mGeometryString.append( attrIter[0] + GML_NAMESPACE_LEN + 1 );
525  }
526  else
527  {
528  mGeometryString.append( attrIter[0] );
529  }
530  mGeometryString.append( "=\"", 2 );
531  mGeometryString.append( attrIter[1] );
532  mGeometryString.append( "\" ", 2 );
533 
534  }
535  mGeometryString.append( ">", 1 );
536  }
537 
538  if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
539  {
540  mParseModeStack.push( Coordinate );
541  mCoorMode = QgsGmlStreamingParser::Coordinate;
542  mStringCash.clear();
543  mCoordinateSeparator = readAttribute( QStringLiteral( "cs" ), attr );
544  if ( mCoordinateSeparator.isEmpty() )
545  {
546  mCoordinateSeparator = ',';
547  }
548  mTupleSeparator = readAttribute( QStringLiteral( "ts" ), attr );
549  if ( mTupleSeparator.isEmpty() )
550  {
551  mTupleSeparator = ' ';
552  }
553  }
554  else if ( isGMLNS &&
555  ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
556  {
557  mParseModeStack.push( QgsGmlStreamingParser::PosList );
558  mCoorMode = QgsGmlStreamingParser::PosList;
559  mStringCash.clear();
560  if ( elDimension == 0 )
561  {
562  QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
563  bool ok;
564  int dimension = srsDimension.toInt( &ok );
565  if ( ok )
566  {
567  elDimension = dimension;
568  }
569  }
570  }
571  else if ( ( parseMode == Feature || parseMode == FeatureTuple ) &&
572  mCurrentFeature &&
573  localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
574  memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
575  {
576  mParseModeStack.push( QgsGmlStreamingParser::Geometry );
577  mFoundUnhandledGeometryElement = false;
578  mGeometryString.clear();
579  }
580  //else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
581  else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
582  {
583  mParseModeStack.push( QgsGmlStreamingParser::BoundingBox );
584  mCurrentExtent = QgsRectangle();
585  mBoundedByNullFound = false;
586  }
587  else if ( parseMode == BoundingBox &&
588  isGMLNS && LOCALNAME_EQUALS( "null" ) )
589  {
590  mParseModeStack.push( QgsGmlStreamingParser::Null );
591  mBoundedByNullFound = true;
592  }
593  else if ( parseMode == BoundingBox &&
594  isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
595  {
596  isGeom = true;
597  mParseModeStack.push( QgsGmlStreamingParser::Envelope );
598  }
599  else if ( parseMode == Envelope &&
600  isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
601  {
602  mParseModeStack.push( QgsGmlStreamingParser::LowerCorner );
603  mStringCash.clear();
604  }
605  else if ( parseMode == Envelope &&
606  isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
607  {
608  mParseModeStack.push( QgsGmlStreamingParser::UpperCorner );
609  mStringCash.clear();
610  }
611  else if ( parseMode == None && !mTypeNamePtr &&
612  LOCALNAME_EQUALS( "Tuple" ) )
613  {
614  Q_ASSERT( !mCurrentFeature );
615  mCurrentFeature = new QgsFeature( mFeatureCount );
616  mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
617  QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
618  mCurrentFeature->setAttributes( attributes );
619  mParseModeStack.push( QgsGmlStreamingParser::Tuple );
620  mCurrentFeatureId.clear();
621  }
622  else if ( parseMode == Tuple )
623  {
624  QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) );
625  QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename );
626  if ( iter != mMapTypeNameToProperties.constEnd() )
627  {
628  mFeatureTupleDepth = mParseDepth;
629  mCurrentTypename = currentTypename;
630  mGeometryAttribute.clear();
631  if ( mCurrentWKB.size() == 0 )
632  {
633  mGeometryAttribute = iter.value().mGeometryAttribute;
634  }
635  mGeometryAttributeBA = mGeometryAttribute.toUtf8();
636  mGeometryAttributePtr = mGeometryAttributeBA.constData();
637  mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
638  mParseModeStack.push( QgsGmlStreamingParser::FeatureTuple );
639  QString id;
640  if ( mGMLNameSpaceURI.isEmpty() )
641  {
642  id = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
643  if ( !id.isEmpty() )
644  {
645  mGMLNameSpaceURI = GML_NAMESPACE;
646  mGMLNameSpaceURIPtr = GML_NAMESPACE;
647  }
648  else
649  {
650  id = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
651  if ( !id.isEmpty() )
652  {
653  mGMLNameSpaceURI = GML32_NAMESPACE;
654  mGMLNameSpaceURIPtr = GML32_NAMESPACE;
655  }
656  }
657  }
658  else
659  id = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
660  if ( !mCurrentFeatureId.isEmpty() )
661  mCurrentFeatureId += '|';
662  mCurrentFeatureId += id;
663  }
664  }
665  else if ( parseMode == None &&
666  localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
667  memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 )
668  {
669  Q_ASSERT( !mCurrentFeature );
670  mCurrentFeature = new QgsFeature( mFeatureCount );
671  mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
672  QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
673  mCurrentFeature->setAttributes( attributes );
674  mParseModeStack.push( QgsGmlStreamingParser::Feature );
675  mCurrentFeatureId = readAttribute( QStringLiteral( "fid" ), attr );
676  if ( mCurrentFeatureId.isEmpty() )
677  {
678  // Figure out if the GML namespace is GML_NAMESPACE or GML32_NAMESPACE
679  // (should happen only for the first features if there's no gml: element
680  // encountered before
681  if ( mGMLNameSpaceURI.isEmpty() )
682  {
683  mCurrentFeatureId = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
684  if ( !mCurrentFeatureId.isEmpty() )
685  {
686  mGMLNameSpaceURI = GML_NAMESPACE;
687  mGMLNameSpaceURIPtr = GML_NAMESPACE;
688  }
689  else
690  {
691  mCurrentFeatureId = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
692  if ( !mCurrentFeatureId.isEmpty() )
693  {
694  mGMLNameSpaceURI = GML32_NAMESPACE;
695  mGMLNameSpaceURIPtr = GML32_NAMESPACE;
696  }
697  }
698  }
699  else
700  mCurrentFeatureId = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
701  }
702  }
703 
704  else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "Box" ) )
705  {
706  isGeom = true;
707  }
708  else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
709  {
710  isGeom = true;
711  }
712  else if ( isGMLNS && LOCALNAME_EQUALS( "LineString" ) )
713  {
714  isGeom = true;
715  }
716  else if ( isGMLNS &&
717  localNameLen == static_cast<int>( strlen( "Polygon" ) ) && memcmp( pszLocalName, "Polygon", localNameLen ) == 0 )
718  {
719  isGeom = true;
720  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
721  }
722  else if ( isGMLNS && LOCALNAME_EQUALS( "MultiPoint" ) )
723  {
724  isGeom = true;
725  mParseModeStack.push( QgsGmlStreamingParser::MultiPoint );
726  //we need one nested list for intermediate WKB
727  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
728  }
729  else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
730  {
731  isGeom = true;
732  mParseModeStack.push( QgsGmlStreamingParser::MultiLine );
733  //we need one nested list for intermediate WKB
734  mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
735  }
736  else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
737  {
738  isGeom = true;
739  mParseModeStack.push( QgsGmlStreamingParser::MultiPolygon );
740  }
741  else if ( parseMode == FeatureTuple )
742  {
743  QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
744  if ( mThematicAttributes.contains( mCurrentTypename + '|' + localName ) )
745  {
746  mParseModeStack.push( QgsGmlStreamingParser::AttributeTuple );
747  mAttributeName = mCurrentTypename + '|' + localName;
748  mStringCash.clear();
749  }
750  }
751  else if ( parseMode == Feature )
752  {
753  QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
754  if ( mThematicAttributes.contains( localName ) )
755  {
756  mParseModeStack.push( QgsGmlStreamingParser::Attribute );
757  mAttributeName = localName;
758  mStringCash.clear();
759  }
760  else
761  {
762  // QGIS server (2.2) is using:
763  // <Attribute value="My description" name="desc"/>
764  if ( localName.compare( QLatin1String( "attribute" ), Qt::CaseInsensitive ) == 0 )
765  {
766  QString name = readAttribute( QStringLiteral( "name" ), attr );
767  if ( mThematicAttributes.contains( name ) )
768  {
769  QString value = readAttribute( QStringLiteral( "value" ), attr );
770  setAttribute( name, value );
771  }
772  }
773  }
774  }
775  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "FeatureCollection" ) )
776  {
777  QString numberReturned = readAttribute( QStringLiteral( "numberReturned" ), attr ); // WFS 2.0
778  if ( numberReturned.isEmpty() )
779  numberReturned = readAttribute( QStringLiteral( "numberOfFeatures" ), attr ); // WFS 1.1
780  bool conversionOk;
781  mNumberReturned = numberReturned.toInt( &conversionOk );
782  if ( !conversionOk )
783  mNumberReturned = -1;
784 
785  QString numberMatched = readAttribute( QStringLiteral( "numberMatched" ), attr ); // WFS 2.0
786  mNumberMatched = numberMatched.toInt( &conversionOk );
787  if ( !conversionOk ) // likely since numberMatched="unknown" is legal
788  mNumberMatched = -1;
789  }
790  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
791  {
792  mIsException = true;
793  mParseModeStack.push( QgsGmlStreamingParser::ExceptionReport );
794  }
795  else if ( mIsException && LOCALNAME_EQUALS( "ExceptionText" ) )
796  {
797  mStringCash.clear();
798  mParseModeStack.push( QgsGmlStreamingParser::ExceptionText );
799  }
800  else if ( mParseDepth == 1 && LOCALNAME_EQUALS( "truncatedResponse" ) )
801  {
802  // e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
803  mTruncatedResponse = true;
804  }
805  else if ( !mGeometryString.empty() &&
806  !LOCALNAME_EQUALS( "exterior" ) &&
807  !LOCALNAME_EQUALS( "interior" ) &&
808  !LOCALNAME_EQUALS( "innerBoundaryIs" ) &&
809  !LOCALNAME_EQUALS( "outerBoundaryIs" ) &&
810  !LOCALNAME_EQUALS( "LinearRing" ) &&
811  !LOCALNAME_EQUALS( "pointMember" ) &&
812  !LOCALNAME_EQUALS( "curveMember" ) &&
813  !LOCALNAME_EQUALS( "lineStringMember" ) &&
814  !LOCALNAME_EQUALS( "polygonMember" ) &&
815  !LOCALNAME_EQUALS( "surfaceMember" ) &&
816  !LOCALNAME_EQUALS( "Curve" ) &&
817  !LOCALNAME_EQUALS( "segments" ) &&
818  !LOCALNAME_EQUALS( "LineStringSegment" ) )
819  {
820  //QgsDebugMsg( "Found unhandled geometry element " + QString::fromUtf8( pszLocalName, localNameLen ) );
821  mFoundUnhandledGeometryElement = true;
822  }
823 
824  if ( !mGeometryString.empty() )
825  isGeom = true;
826 
827  if ( elDimension == 0 && isGeom )
828  {
829  // srsDimension can also be set on the top geometry element
830  // 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
831  QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
832  bool ok;
833  int dimension = srsDimension.toInt( &ok );
834  if ( ok )
835  {
836  elDimension = dimension;
837  }
838  }
839 
840  if ( elDimension != 0 || mDimensionStack.isEmpty() )
841  {
842  mDimensionStack.push( elDimension );
843  }
844  else
845  {
846  mDimensionStack.push( mDimensionStack.back() );
847  }
848 
849  if ( mEpsg == 0 && isGeom )
850  {
851  if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
852  {
853  QgsDebugMsg( QStringLiteral( "error, could not get epsg id" ) );
854  }
855  else
856  {
857  QgsDebugMsg( QStringLiteral( "mEpsg = %1" ).arg( mEpsg ) );
858  }
859  }
860 
861  mParseDepth ++;
862 }
863 
864 void QgsGmlStreamingParser::endElement( const XML_Char *el )
865 {
866  mParseDepth --;
867 
868  const int elLen = static_cast<int>( strlen( el ) );
869  const char *pszSep = strchr( el, NS_SEPARATOR );
870  const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
871  const int nsLen = ( pszSep ) ? ( int )( pszSep - el ) : 0;
872  const int localNameLen = ( pszSep ) ? ( int )( elLen - nsLen ) - 1 : elLen;
873  ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
874 
875  int lastDimension = mDimensionStack.isEmpty() ? 0 : mDimensionStack.pop();
876 
877  const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
878 
879  if ( parseMode == Coordinate && isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
880  {
881  mParseModeStack.pop();
882  }
883  else if ( parseMode == PosList && isGMLNS &&
884  ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
885  {
886  mDimension = lastDimension;
887  mParseModeStack.pop();
888  }
889  else if ( parseMode == AttributeTuple &&
890  mCurrentTypename + '|' + QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
891  {
892  mParseModeStack.pop();
893 
894  setAttribute( mAttributeName, mStringCash );
895  }
896  else if ( parseMode == Attribute && QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
897  {
898  mParseModeStack.pop();
899 
900  setAttribute( mAttributeName, mStringCash );
901  }
902  else if ( parseMode == Geometry &&
903  localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
904  memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
905  {
906  mParseModeStack.pop();
907  if ( mFoundUnhandledGeometryElement )
908  {
909  gdal::ogr_geometry_unique_ptr hGeom( OGR_G_CreateFromGML( mGeometryString.c_str() ) );
910  //QgsDebugMsg( QStringLiteral("for OGR: %1 -> %2").arg(mGeometryString.c_str()).arg(hGeom != nullptr));
911  if ( hGeom )
912  {
913  const int wkbSize = OGR_G_WkbSize( hGeom.get() );
914  unsigned char *pabyBuffer = new unsigned char[ wkbSize ];
915  OGR_G_ExportToIsoWkb( hGeom.get(), wkbNDR, pabyBuffer );
916  QgsGeometry g;
917  g.fromWkb( pabyBuffer, wkbSize );
918  if ( mInvertAxisOrientation )
919  {
920  g.transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
921  }
922  Q_ASSERT( mCurrentFeature );
923  mCurrentFeature->setGeometry( g );
924  }
925  }
926  mGeometryString.clear();
927  }
928  else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
929  {
930  //create bounding box from mStringCash
931  if ( mCurrentExtent.isNull() &&
932  !mBoundedByNullFound &&
933  !createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) )
934  {
935  QgsDebugMsg( QStringLiteral( "creation of bounding box failed" ) );
936  }
937  if ( !mCurrentExtent.isNull() && mLayerExtent.isNull() &&
938  !mCurrentFeature && mFeatureCount == 0 )
939  {
940  mLayerExtent = mCurrentExtent;
941  mCurrentExtent = QgsRectangle();
942  }
943 
944  mParseModeStack.pop();
945  }
946  else if ( parseMode == Null && isGMLNS && LOCALNAME_EQUALS( "null" ) )
947  {
948  mParseModeStack.pop();
949  }
950  else if ( parseMode == Envelope && isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
951  {
952  mParseModeStack.pop();
953  }
954  else if ( parseMode == LowerCorner && isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
955  {
956  QList<QgsPointXY> points;
957  pointsFromPosListString( points, mStringCash, 2 );
958  if ( points.size() == 1 )
959  {
960  mCurrentExtent.setXMinimum( points[0].x() );
961  mCurrentExtent.setYMinimum( points[0].y() );
962  }
963  mParseModeStack.pop();
964  }
965  else if ( parseMode == UpperCorner && isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
966  {
967  QList<QgsPointXY> points;
968  pointsFromPosListString( points, mStringCash, 2 );
969  if ( points.size() == 1 )
970  {
971  mCurrentExtent.setXMaximum( points[0].x() );
972  mCurrentExtent.setYMaximum( points[0].y() );
973  }
974  mParseModeStack.pop();
975  }
976  else if ( parseMode == FeatureTuple && mParseDepth == mFeatureTupleDepth )
977  {
978  mParseModeStack.pop();
979  mFeatureTupleDepth = 0;
980  }
981  else if ( ( parseMode == Tuple && !mTypeNamePtr &&
982  LOCALNAME_EQUALS( "Tuple" ) ) ||
983  ( parseMode == Feature &&
984  localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
985  memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 ) )
986  {
987  Q_ASSERT( mCurrentFeature );
988  if ( !mCurrentFeature->hasGeometry() )
989  {
990  if ( mCurrentWKB.size() > 0 )
991  {
992  QgsGeometry g;
993  g.fromWkb( mCurrentWKB, mCurrentWKB.size() );
994  mCurrentFeature->setGeometry( g );
995  mCurrentWKB = QgsWkbPtr( nullptr, 0 );
996  }
997  else if ( !mCurrentExtent.isEmpty() )
998  {
999  mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
1000  }
1001  }
1002  mCurrentFeature->setValid( true );
1003 
1004  mFeatureList.push_back( QgsGmlFeaturePtrGmlIdPair( mCurrentFeature, mCurrentFeatureId ) );
1005 
1006  mCurrentFeature = nullptr;
1007  ++mFeatureCount;
1008  mParseModeStack.pop();
1009  }
1010  else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
1011  {
1012  QList<QgsPointXY> pointList;
1013  if ( pointsFromString( pointList, mStringCash ) != 0 )
1014  {
1015  //error
1016  }
1017 
1018  if ( pointList.isEmpty() )
1019  return; // error
1020 
1021  if ( parseMode == QgsGmlStreamingParser::Geometry )
1022  {
1023  //directly add WKB point to the feature
1024  if ( getPointWKB( mCurrentWKB, *( pointList.constBegin() ) ) != 0 )
1025  {
1026  //error
1027  }
1028 
1029  if ( mWkbType != QgsWkbTypes::MultiPoint ) //keep multitype in case of geometry type mix
1030  {
1031  mWkbType = QgsWkbTypes::Point;
1032  }
1033  }
1034  else //multipoint, add WKB as fragment
1035  {
1036  QgsWkbPtr wkbPtr( nullptr, 0 );
1037  if ( getPointWKB( wkbPtr, *( pointList.constBegin() ) ) != 0 )
1038  {
1039  //error
1040  }
1041  if ( !mCurrentWKBFragments.isEmpty() )
1042  {
1043  mCurrentWKBFragments.last().push_back( wkbPtr );
1044  }
1045  else
1046  {
1047  QgsDebugMsg( QStringLiteral( "No wkb fragments" ) );
1048  delete [] wkbPtr;
1049  }
1050  }
1051  }
1052  else if ( isGMLNS && ( LOCALNAME_EQUALS( "LineString" ) || LOCALNAME_EQUALS( "LineStringSegment" ) ) )
1053  {
1054  //add WKB point to the feature
1055 
1056  QList<QgsPointXY> pointList;
1057  if ( pointsFromString( pointList, mStringCash ) != 0 )
1058  {
1059  //error
1060  }
1061  if ( parseMode == QgsGmlStreamingParser::Geometry )
1062  {
1063  if ( getLineWKB( mCurrentWKB, pointList ) != 0 )
1064  {
1065  //error
1066  }
1067 
1068  if ( mWkbType != QgsWkbTypes::MultiLineString )//keep multitype in case of geometry type mix
1069  {
1070  mWkbType = QgsWkbTypes::LineString;
1071  }
1072  }
1073  else //multiline, add WKB as fragment
1074  {
1075  QgsWkbPtr wkbPtr( nullptr, 0 );
1076  if ( getLineWKB( wkbPtr, pointList ) != 0 )
1077  {
1078  //error
1079  }
1080  if ( !mCurrentWKBFragments.isEmpty() )
1081  {
1082  mCurrentWKBFragments.last().push_back( wkbPtr );
1083  }
1084  else
1085  {
1086  QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1087  delete [] wkbPtr;
1088  }
1089  }
1090  }
1091  else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) &&
1092  isGMLNS && LOCALNAME_EQUALS( "LinearRing" ) )
1093  {
1094  QList<QgsPointXY> pointList;
1095  if ( pointsFromString( pointList, mStringCash ) != 0 )
1096  {
1097  //error
1098  }
1099 
1100  QgsWkbPtr wkbPtr( nullptr, 0 );
1101  if ( getRingWKB( wkbPtr, pointList ) != 0 )
1102  {
1103  //error
1104  }
1105 
1106  if ( !mCurrentWKBFragments.isEmpty() )
1107  {
1108  mCurrentWKBFragments.last().push_back( wkbPtr );
1109  }
1110  else
1111  {
1112  delete[] wkbPtr;
1113  QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1114  }
1115  }
1116  else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) && isGMLNS &&
1117  LOCALNAME_EQUALS( "Polygon" ) )
1118  {
1119  if ( mWkbType != QgsWkbTypes::MultiPolygon )//keep multitype in case of geometry type mix
1120  {
1121  mWkbType = QgsWkbTypes::Polygon;
1122  }
1123 
1124  if ( parseMode == Geometry )
1125  {
1126  createPolygonFromFragments();
1127  }
1128  }
1129  else if ( parseMode == MultiPoint && isGMLNS &&
1130  LOCALNAME_EQUALS( "MultiPoint" ) )
1131  {
1132  mWkbType = QgsWkbTypes::MultiPoint;
1133  mParseModeStack.pop();
1134  createMultiPointFromFragments();
1135  }
1136  else if ( parseMode == MultiLine && isGMLNS &&
1137  ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
1138  {
1139  mWkbType = QgsWkbTypes::MultiLineString;
1140  mParseModeStack.pop();
1141  createMultiLineFromFragments();
1142  }
1143  else if ( parseMode == MultiPolygon && isGMLNS &&
1144  ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
1145  {
1146  mWkbType = QgsWkbTypes::MultiPolygon;
1147  mParseModeStack.pop();
1148  createMultiPolygonFromFragments();
1149  }
1150  else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
1151  {
1152  mParseModeStack.pop();
1153  }
1154  else if ( parseMode == ExceptionText && LOCALNAME_EQUALS( "ExceptionText" ) )
1155  {
1156  mExceptionText = mStringCash;
1157  mParseModeStack.pop();
1158  }
1159 
1160  if ( !mGeometryString.empty() )
1161  {
1162  mGeometryString.append( "</", 2 );
1163  mGeometryString.append( pszLocalName, localNameLen );
1164  mGeometryString.append( ">", 1 );
1165  }
1166 
1167 }
1168 
1169 void QgsGmlStreamingParser::characters( const XML_Char *chars, int len )
1170 {
1171  //save chars in mStringCash attribute mode or coordinate mode
1172  if ( mParseModeStack.isEmpty() )
1173  {
1174  return;
1175  }
1176 
1177  if ( !mGeometryString.empty() )
1178  {
1179  mGeometryString.append( chars, len );
1180  }
1181 
1182  QgsGmlStreamingParser::ParseMode parseMode = mParseModeStack.top();
1183  if ( parseMode == QgsGmlStreamingParser::Attribute ||
1184  parseMode == QgsGmlStreamingParser::AttributeTuple ||
1185  parseMode == QgsGmlStreamingParser::Coordinate ||
1186  parseMode == QgsGmlStreamingParser::PosList ||
1187  parseMode == QgsGmlStreamingParser::LowerCorner ||
1188  parseMode == QgsGmlStreamingParser::UpperCorner ||
1189  parseMode == QgsGmlStreamingParser::ExceptionText )
1190  {
1191  mStringCash.append( QString::fromUtf8( chars, len ) );
1192  }
1193 }
1194 
1195 void QgsGmlStreamingParser::setAttribute( const QString &name, const QString &value )
1196 {
1197  //find index with attribute name
1198  QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.constFind( name );
1199  bool conversionOk = true;
1200  if ( att_it != mThematicAttributes.constEnd() )
1201  {
1202  QVariant var;
1203  switch ( att_it.value().second.type() )
1204  {
1205  case QVariant::Double:
1206  var = QVariant( value.toDouble( &conversionOk ) );
1207  break;
1208  case QVariant::Int:
1209  var = QVariant( value.toInt( &conversionOk ) );
1210  break;
1211  case QVariant::LongLong:
1212  var = QVariant( value.toLongLong( &conversionOk ) );
1213  break;
1214  case QVariant::DateTime:
1215  var = QVariant( QDateTime::fromString( value, Qt::ISODate ) );
1216  break;
1217  default: //string type is default
1218  var = QVariant( value );
1219  break;
1220  }
1221  if ( ! conversionOk ) // Assume is NULL
1222  {
1223  var = QVariant();
1224  }
1225  Q_ASSERT( mCurrentFeature );
1226  mCurrentFeature->setAttribute( att_it.value().first, var );
1227  }
1228 }
1229 
1230 int QgsGmlStreamingParser::readEpsgFromAttribute( int &epsgNr, const XML_Char **attr )
1231 {
1232  int i = 0;
1233  while ( attr[i] )
1234  {
1235  if ( strcmp( attr[i], "srsName" ) == 0 )
1236  {
1237  QString epsgString( attr[i + 1] );
1238  QString epsgNrString;
1239  bool bIsUrn = false;
1240  if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) ) //e.g. geoserver: "http://www.opengis.net/gml/srs/epsg.xml#4326"
1241  {
1242  epsgNrString = epsgString.section( '#', 1, 1 );
1243  }
1244  // WFS >= 1.1
1245  else if ( epsgString.startsWith( QLatin1String( "urn:ogc:def:crs:EPSG:" ) ) ||
1246  epsgString.startsWith( QLatin1String( "urn:x-ogc:def:crs:EPSG:" ) ) )
1247  {
1248  bIsUrn = true;
1249  epsgNrString = epsgString.split( ':' ).last();
1250  }
1251  else if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/def/crs/EPSG/" ) ) ) //e.g. geoserver: "http://www.opengis.net/def/crs/EPSG/4326"
1252  {
1253  bIsUrn = true;
1254  epsgNrString = epsgString.split( '/' ).last();
1255  }
1256  else //e.g. umn mapserver: "EPSG:4326">
1257  {
1258  epsgNrString = epsgString.section( ':', 1, 1 );
1259  }
1260  bool conversionOk;
1261  int eNr = epsgNrString.toInt( &conversionOk );
1262  if ( !conversionOk )
1263  {
1264  return 1;
1265  }
1266  epsgNr = eNr;
1267  mSrsName = epsgString;
1268 
1269  QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( epsgNr ) );
1270  if ( crs.isValid() )
1271  {
1272  if ( ( ( mAxisOrientationLogic == Honour_EPSG_if_urn && bIsUrn ) ||
1273  mAxisOrientationLogic == Honour_EPSG ) && crs.hasAxisInverted() )
1274  {
1275  mInvertAxisOrientation = !mInvertAxisOrientationRequest;
1276  }
1277  }
1278 
1279  return 0;
1280  }
1281  ++i;
1282  }
1283  return 2;
1284 }
1285 
1286 QString QgsGmlStreamingParser::readAttribute( const QString &attributeName, const XML_Char **attr ) const
1287 {
1288  int i = 0;
1289  while ( attr[i] )
1290  {
1291  if ( attributeName.compare( attr[i] ) == 0 )
1292  {
1293  return QString::fromUtf8( attr[i + 1] );
1294  }
1295  i += 2;
1296  }
1297  return QString();
1298 }
1299 
1300 bool QgsGmlStreamingParser::createBBoxFromCoordinateString( QgsRectangle &r, const QString &coordString ) const
1301 {
1302  QList<QgsPointXY> points;
1303  if ( pointsFromCoordinateString( points, coordString ) != 0 )
1304  {
1305  return false;
1306  }
1307 
1308  if ( points.size() < 2 )
1309  {
1310  return false;
1311  }
1312 
1313  r.set( points[0], points[1] );
1314 
1315  return true;
1316 }
1317 
1318 int QgsGmlStreamingParser::pointsFromCoordinateString( QList<QgsPointXY> &points, const QString &coordString ) const
1319 {
1320  //tuples are separated by space, x/y by ','
1321 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1322  QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
1323 #else
1324  QStringList tuples = coordString.split( mTupleSeparator, Qt::SkipEmptyParts );
1325 #endif
1326  QStringList tuples_coordinates;
1327  double x, y;
1328  bool conversionSuccess;
1329 
1330  QStringList::const_iterator tupleIterator;
1331  for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
1332  {
1333 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1334  tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
1335 #else
1336  tuples_coordinates = tupleIterator->split( mCoordinateSeparator, Qt::SkipEmptyParts );
1337 #endif
1338  if ( tuples_coordinates.size() < 2 )
1339  {
1340  continue;
1341  }
1342  x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
1343  if ( !conversionSuccess )
1344  {
1345  continue;
1346  }
1347  y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
1348  if ( !conversionSuccess )
1349  {
1350  continue;
1351  }
1352  points.push_back( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1353  }
1354  return 0;
1355 }
1356 
1357 int QgsGmlStreamingParser::pointsFromPosListString( QList<QgsPointXY> &points, const QString &coordString, int dimension ) const
1358 {
1359  // coordinates separated by spaces
1360 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1361  QStringList coordinates = coordString.split( ' ', QString::SkipEmptyParts );
1362 #else
1363  QStringList coordinates = coordString.split( ' ', Qt::SkipEmptyParts );
1364 #endif
1365 
1366  if ( coordinates.size() % dimension != 0 )
1367  {
1368  QgsDebugMsg( QStringLiteral( "Wrong number of coordinates" ) );
1369  }
1370 
1371  int ncoor = coordinates.size() / dimension;
1372  for ( int i = 0; i < ncoor; i++ )
1373  {
1374  bool conversionSuccess;
1375  double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
1376  if ( !conversionSuccess )
1377  {
1378  continue;
1379  }
1380  double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
1381  if ( !conversionSuccess )
1382  {
1383  continue;
1384  }
1385  points.append( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1386  }
1387  return 0;
1388 }
1389 
1390 int QgsGmlStreamingParser::pointsFromString( QList<QgsPointXY> &points, const QString &coordString ) const
1391 {
1392  if ( mCoorMode == QgsGmlStreamingParser::Coordinate )
1393  {
1394  return pointsFromCoordinateString( points, coordString );
1395  }
1396  else if ( mCoorMode == QgsGmlStreamingParser::PosList )
1397  {
1398  return pointsFromPosListString( points, coordString, mDimension ? mDimension : 2 );
1399  }
1400  return 1;
1401 }
1402 
1403 int QgsGmlStreamingParser::getPointWKB( QgsWkbPtr &wkbPtr, const QgsPointXY &point ) const
1404 {
1405  int wkbSize = 1 + sizeof( int ) + 2 * sizeof( double );
1406  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1407 
1408  QgsWkbPtr fillPtr( wkbPtr );
1409  fillPtr << mEndian << QgsWkbTypes::Point << point.x() << point.y();
1410 
1411  return 0;
1412 }
1413 
1414 int QgsGmlStreamingParser::getLineWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &lineCoordinates ) const
1415 {
1416  int wkbSize = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
1417  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1418 
1419  QgsWkbPtr fillPtr( wkbPtr );
1420 
1421  fillPtr << mEndian << QgsWkbTypes::LineString << lineCoordinates.size();
1422 
1423  QList<QgsPointXY>::const_iterator iter;
1424  for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
1425  {
1426  fillPtr << iter->x() << iter->y();
1427  }
1428 
1429  return 0;
1430 }
1431 
1432 int QgsGmlStreamingParser::getRingWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &ringCoordinates ) const
1433 {
1434  int wkbSize = sizeof( int ) + ringCoordinates.size() * 2 * sizeof( double );
1435  wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1436 
1437  QgsWkbPtr fillPtr( wkbPtr );
1438 
1439  fillPtr << ringCoordinates.size();
1440 
1441  QList<QgsPointXY>::const_iterator iter;
1442  for ( iter = ringCoordinates.constBegin(); iter != ringCoordinates.constEnd(); ++iter )
1443  {
1444  fillPtr << iter->x() << iter->y();
1445  }
1446 
1447  return 0;
1448 }
1449 
1450 int QgsGmlStreamingParser::createMultiLineFromFragments()
1451 {
1452  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1453  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1454 
1455  QgsWkbPtr wkbPtr( mCurrentWKB );
1456 
1457  wkbPtr << mEndian << QgsWkbTypes::MultiLineString << mCurrentWKBFragments.constBegin()->size();
1458 
1459  //copy (and delete) all the wkb fragments
1460  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1461  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1462  {
1463  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1464  wkbPtr += wkbIt->size();
1465  delete[] *wkbIt;
1466  }
1467 
1468  mCurrentWKBFragments.clear();
1469  mWkbType = QgsWkbTypes::MultiLineString;
1470  return 0;
1471 }
1472 
1473 int QgsGmlStreamingParser::createMultiPointFromFragments()
1474 {
1475  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1476  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1477 
1478  QgsWkbPtr wkbPtr( mCurrentWKB );
1479  wkbPtr << mEndian << QgsWkbTypes::MultiPoint << mCurrentWKBFragments.constBegin()->size();
1480 
1481  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1482  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1483  {
1484  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1485  wkbPtr += wkbIt->size();
1486  delete[] *wkbIt;
1487  }
1488 
1489  mCurrentWKBFragments.clear();
1490  mWkbType = QgsWkbTypes::MultiPoint;
1491  return 0;
1492 }
1493 
1494 
1495 int QgsGmlStreamingParser::createPolygonFromFragments()
1496 {
1497  int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1498  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1499 
1500  QgsWkbPtr wkbPtr( mCurrentWKB );
1501  wkbPtr << mEndian << QgsWkbTypes::Polygon << mCurrentWKBFragments.constBegin()->size();
1502 
1503  QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1504  for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1505  {
1506  memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1507  wkbPtr += wkbIt->size();
1508  delete[] *wkbIt;
1509  }
1510 
1511  mCurrentWKBFragments.clear();
1512  mWkbType = QgsWkbTypes::Polygon;
1513  return 0;
1514 }
1515 
1516 int QgsGmlStreamingParser::createMultiPolygonFromFragments()
1517 {
1518  int size = 0;
1519  size += 1 + 2 * sizeof( int );
1520  size += totalWKBFragmentSize();
1521  size += mCurrentWKBFragments.size() * ( 1 + 2 * sizeof( int ) ); //fragments are just the rings
1522 
1523  mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1524 
1525  QgsWkbPtr wkbPtr( mCurrentWKB );
1526  wkbPtr << ( char ) mEndian << QgsWkbTypes::MultiPolygon << mCurrentWKBFragments.size();
1527 
1528  //have outer and inner iterators
1529  QList< QList<QgsWkbPtr> >::const_iterator outerWkbIt = mCurrentWKBFragments.constBegin();
1530 
1531  for ( ; outerWkbIt != mCurrentWKBFragments.constEnd(); ++outerWkbIt )
1532  {
1533  //new polygon
1534  wkbPtr << ( char ) mEndian << QgsWkbTypes::Polygon << outerWkbIt->size();
1535 
1536  QList<QgsWkbPtr>::const_iterator innerWkbIt = outerWkbIt->constBegin();
1537  for ( ; innerWkbIt != outerWkbIt->constEnd(); ++innerWkbIt )
1538  {
1539  memcpy( wkbPtr, *innerWkbIt, innerWkbIt->size() );
1540  wkbPtr += innerWkbIt->size();
1541  delete[] *innerWkbIt;
1542  }
1543  }
1544 
1545  mCurrentWKBFragments.clear();
1546  mWkbType = QgsWkbTypes::MultiPolygon;
1547  return 0;
1548 }
1549 
1550 int QgsGmlStreamingParser::totalWKBFragmentSize() const
1551 {
1552  int result = 0;
1553  const auto constMCurrentWKBFragments = mCurrentWKBFragments;
1554  for ( const QList<QgsWkbPtr> &list : constMCurrentWKBFragments )
1555  {
1556  const auto constList = list;
1557  for ( const QgsWkbPtr &i : constList )
1558  {
1559  result += i.size();
1560  }
1561  }
1562  return result;
1563 }
QgsGml::totalStepsUpdate
void totalStepsUpdate(int totalSteps)
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
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:2813
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
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:78
QgsGml::dataReadProgress
void dataReadProgress(int progress)
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
QgsGeometry::isNull
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:51
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:73
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
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:67
QgsFeature::setValid
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:190
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
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:151
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:813
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
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
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsSetRequestInitiatorClass
#define QgsSetRequestInitiatorClass(request, _class)
Definition: qgsnetworkaccessmanager.h:41
QgsApplication::authManager
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
Definition: qgsapplication.cpp:1282
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
NS_SEPARATOR
const char NS_SEPARATOR
Definition: qgsgmlschema.cpp:33
QgsWkbTypes::MultiLineString
@ MultiLineString
Definition: qgswkbtypes.h:77
QgsGmlStreamingParser::~QgsGmlStreamingParser
~QgsGmlStreamingParser()
Definition: qgsgml.cpp:422
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
QgsFeature::setGeometry
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:139
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:164
qgsnetworkaccessmanager.h
QgsGmlStreamingParser::getEPSGCode
int getEPSGCode() const
Returns the EPSG code, or 0 if unknown.
Definition: qgsgml.h:122
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:130
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:924
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1250
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
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:55
QgsWkbPtr
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:121
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:206
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsRectangle::setYMaximum
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsFeature::setAttribute
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
Definition: qgsfeature.cpp:213
qgsgml.h
qgsgeometry.h
QgsGmlStreamingParser::AxisOrientationLogic
AxisOrientationLogic
Axis orientation logic.
Definition: qgsgml.h:72
QgsRectangle::setYMinimum
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
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:199
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:134
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:275
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:76
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:229
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:137
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:92
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:996
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsWkbTypes
Handles storage of information regarding WKB types and their properties.
Definition: qgswkbtypes.h:42
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:129
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:131
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
GML_NAMESPACE
#define GML_NAMESPACE
Definition: qgsgmlschema.cpp:34
qgsmessagelog.h