QGIS API Documentation  2.17.0-Master (0497e4a)
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 }
void clear()
void unionRect(const QgsRectangle &rect)
Updates rectangle to include passed argument.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
int getEPSGCode() const
Return the EPSG code, or 0 if unknown.
Definition: qgsgml.h:109
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QString & append(QChar ch)
QgsCoordinateReferenceSystem crsByOgcWmsCrs(const QString &ogcCrs) const
Returns the CRS from a given OGC WMS-format Coordinate Reference System string.
int size() const
Return number of items.
Definition: qgsfield.cpp:407
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
bool contains(const Key &key) const
static QgsAuthManager * instance()
Enforce singleton pattern.
AxisOrientationLogic
Axis orientation logic.
Definition: qgsgml.h:69
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:172
void push_back(const T &value)
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer&#39;s length...
void setWindowModality(Qt::WindowModality windowModality)
QString errorString() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int numberReturned() const
Return WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found...
Definition: qgsgml.h:124
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
void push(const T &t)
const T & at(int i) const
int size() const
bool isEmpty() const
int numberMatched() const
Return WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found.
Definition: qgsgml.h:121
Container of fields for a vector layer.
Definition: qgsfield.h:252
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:115
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:222
WkbType
Used for symbology operations.
Definition: qgis.h:61
static const char * GML32_NAMESPACE
Definition: qgsgml.cpp:41
void dataReadProgress(int progress)
const_iterator constFind(const Key &key) const
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
The QGis class provides global constants for use throughout the application.
Definition: qgis.h:40
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
void clear()
double toDouble(bool *ok) const
static endian_t endian()
Returns whether this machine uses big or little endian.
QString tr(const char *sourceText, const char *disambiguation, int n)
#define LOCALNAME_EQUALS(string_constant)
Definition: qgsgml.cpp:456
int size() const
double y() const
Get the y value of the point.
Definition: qgspoint.h:193
bool isNull() const
void totalStepsUpdate(int totalSteps)
T value(int i) const
void set(const QgsPoint &p1, const QgsPoint &p2)
Set the rectangle from two QgsPoints.
void clear()
void setGeometry(const QgsGeometry &geom)
Set this feature&#39;s geometry from another QgsGeometry object.
Definition: qgsfeature.cpp:124
Honour EPSG axis order.
Definition: qgsgml.h:74
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
void append(const T &value)
QString fromUtf8(const char *str, int size)
int getFeatures(const QString &uri, QGis::WkbType *wkbType, QgsRectangle *extent=nullptr, const QString &userName=QString(), const QString &password=QString(), const QString &authcfg=QString())
Does the Http GET request to the wfs server Supports only UTF-8, UTF-16, ISO-8859-1, ISO-8859-1 XML encodings.
Definition: qgsgml.cpp:63
void dataProgressAndSteps(int progress, int totalSteps)
static QString stripNS(const QString &string)
Definition: qgsgml.cpp:317
bool isEmpty() const
test if rectangle is empty.
QgsGml(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields)
Definition: qgsgml.cpp:43
int toInt(bool *ok, int base) const
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:177
bool isEmpty() const
bool isEmpty() const
const_iterator constEnd() const
const char * constData() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QByteArray readAll()
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
const T & value() const
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
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
QDateTime fromString(const QString &string, Qt::DateFormat format)
QgsGeometry * geometry()
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:76
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:204
QString mid(int position, int n) const
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:182
bool processData(const QByteArray &data, bool atEnd, QString &errorMsg)
Process a new chunk of data.
Definition: qgsgml.cpp:433
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
T & last()
Class for storing a coordinate reference system (CRS)
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
int length() const
static QgsGeometry * fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QgsGmlStreamingParser(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields, AxisOrientationLogic axisOrientationLogic=Honour_EPSG_if_urn, bool invertAxisOrientation=false)
Constructor.
Definition: qgsgml.cpp: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.
int size() const
Definition: qgswkbptr.h:83
QString section(QChar sep, int start, int end, QFlags< QString::SectionFlag > flags) const
NetworkError error() const
QgsCoordinateReferenceSystem crs() const
Returns features spatial reference system.
Definition: qgsgml.cpp:249
QGis::WkbType wkbType() const
Return the geometry type.
Definition: qgsgml.h:118
iterator insert(const Key &key, const T &value)
void show()
static const char NS_SEPARATOR
Definition: qgsgml.cpp:39
QNetworkReply * get(const QNetworkRequest &request)
const_iterator constEnd() const
const_iterator constBegin() const
int size() const
A vector of attributes.
Definition: qgsfeature.h:115
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int compare(const QString &other) const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
~QgsGml()
Definition: qgsgml.cpp:59
static QgsCRSCache * instance()
Returns a pointer to the QgsCRSCache singleton.
Definition: qgscrscache.cpp:91
qlonglong toLongLong(bool *ok, int base) const
int size() const
double x() const
Get the x value of the point.
Definition: qgspoint.h:185
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:167
bool axisInverted() const
Returns whether axis is inverted (eg.
const T value(const Key &key) const
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
T & top()
QByteArray toUtf8() const