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