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