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