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