LCOV - code coverage report
Current view: top level - core - qgsgml.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 1010 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

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

Generated by: LCOV version 1.14