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