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