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