29#include <QNetworkRequest>
30#include <QNetworkReply>
31#include <QProgressDialog>
36#include <QRegularExpression>
42static const char NS_SEPARATOR =
'?';
43static const char *
GML_NAMESPACE =
"http://www.opengis.net/gml";
48 const QString &geometryAttribute,
50 : mParser(
typeName, geometryAttribute, fields )
54 const int index = mTypeName.indexOf(
':' );
55 if ( index != -1 && index < mTypeName.length() )
57 mTypeName = mTypeName.mid( index + 1 );
66 QNetworkRequest request( uri );
69 if ( !authcfg.isEmpty() )
74 tr(
"GML Getfeature network request update failed for authcfg %1" ).arg( authcfg ),
81 else if ( !userName.isNull() || !password.isNull() )
83 request.setRawHeader(
"Authorization",
"Basic " + QStringLiteral(
"%1:%2" ).arg( userName, password ).toLatin1().toBase64() );
87 if ( !authcfg.isEmpty() )
93 tr(
"GML Getfeature network reply update failed for authcfg %1" ).arg( authcfg ),
101 connect( reply, &QNetworkReply::finished,
this, &QgsGml::setFinished );
102 connect( reply, &QNetworkReply::downloadProgress,
this, &QgsGml::handleProgressEvent );
105 QProgressDialog *progressDialog =
nullptr;
106 QWidget *mainWindow =
nullptr;
107 const QWidgetList topLevelWidgets = qApp->topLevelWidgets();
108 for ( QWidgetList::const_iterator it = topLevelWidgets.constBegin(); it != topLevelWidgets.constEnd(); ++it )
110 if ( ( *it )->objectName() == QLatin1String(
"QgisApp" ) )
118 progressDialog =
new QProgressDialog( tr(
"Loading GML data\n%1" ).arg( mTypeName ), tr(
"Abort" ), 0, 0, mainWindow );
119 progressDialog->setWindowModality( Qt::ApplicationModal );
122 connect( progressDialog, &QProgressDialog::canceled,
this, &QgsGml::setFinished );
123 progressDialog->show();
133 const QByteArray readData = reply->readAll();
134 if ( !readData.isEmpty() )
137 if ( !mParser.
processData( readData, atEnd, errorMsg ) )
141 QCoreApplication::processEvents();
144 fillMapsFromParser();
146 const QNetworkReply::NetworkError replyError = reply->error();
147 const QString replyErrorString = reply->errorString();
150 delete progressDialog;
155 tr(
"GML Getfeature network request failed with error: %1" ).arg( replyErrorString ),
169 calculateExtentFromFeatures();
184 if ( !mParser.
processData( data,
true , errorMsg ) )
187 fillMapsFromParser();
197void QgsGml::fillMapsFromParser()
200 const auto constFeatures = features;
204 const QString &gmlId = featPair.second;
205 mFeatures.insert( feat->
id(), feat );
206 if ( !gmlId.isEmpty() )
208 mIdMap.insert( feat->
id(), gmlId );
213void QgsGml::setFinished()
218void QgsGml::handleProgressEvent( qint64 progress, qint64 totalSteps )
220 if ( totalSteps < 0 )
230void QgsGml::calculateExtentFromFeatures()
232 if ( mFeatures.empty() )
239 bool bboxInitialized =
false;
241 for (
int i = 0; i < mFeatures.size(); ++i )
243 currentFeature = mFeatures[i];
244 if ( !currentFeature )
248 currentGeometry = currentFeature->
geometry();
249 if ( !currentGeometry.
isNull() )
251 if ( !bboxInitialized )
254 bboxInitialized =
true;
279 const QString &geometryAttribute,
282 bool invertAxisOrientation )
284 , mTypeNameBA( mTypeName.toUtf8() )
285 , mTypeNamePtr( mTypeNameBA.constData() )
286 , mTypeNameUTF8Len( strlen( mTypeNamePtr ) )
287 , mWkbType(
Qgis::WkbType::Unknown )
288 , mGeometryAttribute( geometryAttribute )
289 , mGeometryAttributeBA( geometryAttribute.toUtf8() )
290 , mGeometryAttributePtr( mGeometryAttributeBA.constData() )
291 , mGeometryAttributeUTF8Len( strlen( mGeometryAttributePtr ) )
293 , mIsException( false )
294 , mTruncatedResponse( false )
296 , mFeatureTupleDepth( 0 )
298 , mCurrentWKB( nullptr, 0 )
299 , mBoundedByNullFound( false )
301 , mCoorMode( Coordinate )
303 , mAxisOrientationLogic( axisOrientationLogic )
304 , mInvertAxisOrientationRequest( invertAxisOrientation )
305 , mInvertAxisOrientation( invertAxisOrientation )
306 , mNumberReturned( -1 )
307 , mNumberMatched( -1 )
308 , mFoundUnhandledGeometryElement( false )
310 mThematicAttributes.clear();
311 for (
int i = 0; i < fields.
size(); i++ )
313 mThematicAttributes.insert( fields.
at( i ).
name(), qMakePair( i, fields.
at( i ) ) );
318 const int index = mTypeName.indexOf(
':' );
319 if ( index != -1 && index < mTypeName.length() )
321 mTypeName = mTypeName.mid( index + 1 );
322 mTypeNameBA = mTypeName.toUtf8();
323 mTypeNamePtr = mTypeNameBA.constData();
324 mTypeNameUTF8Len = strlen( mTypeNamePtr );
330static QString stripNS(
const QString &
string )
332 const int index =
string.indexOf(
':' );
333 if ( index != -1 && index <
string.length() )
335 return string.mid( index + 1 );
342 const QMap< QString, QPair<QString, QString> > &mapFieldNameToSrcLayerNameFieldName,
344 bool invertAxisOrientation )
345 : mLayerProperties( layerProperties )
346 , mTypeNameUTF8Len( 0 )
347 , mWkbType(
Qgis::WkbType::Unknown )
348 , mGeometryAttributeUTF8Len( 0 )
350 , mIsException( false )
351 , mTruncatedResponse( false )
353 , mFeatureTupleDepth( 0 )
355 , mCurrentWKB( nullptr, 0 )
356 , mBoundedByNullFound( false )
358 , mCoorMode( Coordinate )
360 , mAxisOrientationLogic( axisOrientationLogic )
361 , mInvertAxisOrientationRequest( invertAxisOrientation )
362 , mInvertAxisOrientation( invertAxisOrientation )
363 , mNumberReturned( -1 )
364 , mNumberMatched( -1 )
365 , mFoundUnhandledGeometryElement( false )
367 mThematicAttributes.clear();
368 for (
int i = 0; i < fields.
size(); i++ )
370 const QMap< QString, QPair<QString, QString> >::const_iterator att_it = mapFieldNameToSrcLayerNameFieldName.constFind( fields.
at( i ).
name() );
371 if ( att_it != mapFieldNameToSrcLayerNameFieldName.constEnd() )
373 if ( mLayerProperties.size() == 1 )
374 mThematicAttributes.insert( att_it.value().second, qMakePair( i, fields.
at( i ) ) );
376 mThematicAttributes.insert( stripNS( att_it.value().first ) +
"|" + att_it.value().second, qMakePair( i, fields.
at( i ) ) );
379 bool alreadyFoundGeometry =
false;
380 for (
int i = 0; i < mLayerProperties.size(); i++ )
383 if ( !mLayerProperties[i].mGeometryAttribute.isEmpty() )
385 if ( alreadyFoundGeometry )
387 QgsDebugMsgLevel( QStringLiteral(
"Will ignore geometry field %1 from typename %2" ).
388 arg( mLayerProperties[i].mGeometryAttribute, mLayerProperties[i].mName ), 2 );
389 mLayerProperties[i].mGeometryAttribute.clear();
391 alreadyFoundGeometry =
true;
393 mMapTypeNameToProperties.insert( stripNS( mLayerProperties[i].mName ), mLayerProperties[i] );
396 if ( mLayerProperties.size() == 1 )
398 mTypeName = mLayerProperties[0].mName;
399 mGeometryAttribute = mLayerProperties[0].mGeometryAttribute;
400 mGeometryAttributeBA = mGeometryAttribute.toUtf8();
401 mGeometryAttributePtr = mGeometryAttributeBA.constData();
402 mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
403 const int index = mTypeName.indexOf(
':' );
404 if ( index != -1 && index < mTypeName.length() )
406 mTypeName = mTypeName.mid( index + 1 );
408 mTypeNameBA = mTypeName.toUtf8();
409 mTypeNamePtr = mTypeNameBA.constData();
410 mTypeNameUTF8Len = strlen( mTypeNamePtr );
421 XML_ParserFree( mParser );
424 const auto constMFeatureList = mFeatureList;
427 delete featPair.first;
430 delete mCurrentFeature;
446 QByteArray data = pdata;
451 QString strData = mCodec->toUnicode( pdata );
452 data = strData.toUtf8();
455 if ( XML_Parse( mParser, data, data.size(), atEnd ) == XML_STATUS_ERROR )
457 const XML_Error errorCode = XML_GetErrorCode( mParser );
458 if ( !mCodec && errorCode == XML_ERROR_UNKNOWN_ENCODING )
462 const thread_local QRegularExpression reEncoding( QStringLiteral(
"<?xml.*encoding=['\"]([^'\"]*)['\"].*?>" ),
463 QRegularExpression::CaseInsensitiveOption );
464 QRegularExpressionMatch match = reEncoding.match( pdata );
465 const QString encoding = match.hasMatch() ? match.captured( 1 ) : QString();
466 mCodec = !encoding.isEmpty() ? QTextCodec::codecForName( encoding.toLatin1() ) :
nullptr;
470 XML_ParserFree( mParser );
472 createParser( QByteArrayLiteral(
"UTF-8" ) );
478 errorMsg = QObject::tr(
"Error: %1 on line %2, column %3" )
479 .arg( XML_ErrorString( errorCode ) )
480 .arg( XML_GetCurrentLineNumber( mParser ) )
481 .arg( XML_GetCurrentColumnNumber( mParser ) );
491 QVector<QgsGmlFeaturePtrGmlIdPair> ret = mFeatureList;
492 mFeatureList.clear();
496#define LOCALNAME_EQUALS(string_constant) \
497 ( localNameLen == static_cast<int>(strlen( string_constant )) && memcmp(pszLocalName, string_constant, localNameLen) == 0 )
499void QgsGmlStreamingParser::startElement(
const XML_Char *el,
const XML_Char **attr )
501 const int elLen =
static_cast<int>( strlen( el ) );
502 const char *pszSep = strchr( el, NS_SEPARATOR );
503 const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
504 const int nsLen = ( pszSep ) ? (
int )( pszSep - el ) : 0;
505 const int localNameLen = ( pszSep ) ? (
int )( elLen - nsLen ) - 1 : elLen;
506 const ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
510 if ( !mGMLNameSpaceURIPtr && pszSep )
524 const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
527 if ( parseMode == Geometry || parseMode == Coordinate || parseMode == PosList ||
528 parseMode == MultiPoint || parseMode == MultiLine || parseMode == MultiPolygon )
530 mGeometryString.append(
"<", 1 );
531 mGeometryString.append( pszLocalName, localNameLen );
532 mGeometryString.append(
" ", 1 );
533 for (
const XML_Char **attrIter = attr; attrIter && *attrIter; attrIter += 2 )
535 const size_t nAttrLen = strlen( attrIter[0] );
538 if ( nAttrLen > GML32_NAMESPACE_LEN &&
539 attrIter[0][GML32_NAMESPACE_LEN] ==
'?' &&
542 mGeometryString.append(
"gml:" );
543 mGeometryString.append( attrIter[0] + GML32_NAMESPACE_LEN + 1 );
545 else if ( nAttrLen > GML_NAMESPACE_LEN &&
546 attrIter[0][GML_NAMESPACE_LEN] ==
'?' &&
547 memcmp( attrIter[0],
GML_NAMESPACE, GML_NAMESPACE_LEN ) == 0 )
549 mGeometryString.append(
"gml:" );
550 mGeometryString.append( attrIter[0] + GML_NAMESPACE_LEN + 1 );
554 mGeometryString.append( attrIter[0] );
556 mGeometryString.append(
"=\"", 2 );
557 mGeometryString.append( attrIter[1] );
558 mGeometryString.append(
"\" ", 2 );
561 mGeometryString.append(
">", 1 );
566 mParseModeStack.push( Coordinate );
567 mCoorMode = QgsGmlStreamingParser::Coordinate;
569 mCoordinateSeparator = readAttribute( QStringLiteral(
"cs" ), attr );
570 if ( mCoordinateSeparator.isEmpty() )
572 mCoordinateSeparator =
',';
574 mTupleSeparator = readAttribute( QStringLiteral(
"ts" ), attr );
575 if ( mTupleSeparator.isEmpty() )
577 mTupleSeparator =
' ';
583 mParseModeStack.push( QgsGmlStreamingParser::PosList );
584 mCoorMode = QgsGmlStreamingParser::PosList;
586 if ( elDimension == 0 )
588 const QString srsDimension = readAttribute( QStringLiteral(
"srsDimension" ), attr );
590 const int dimension = srsDimension.toInt( &ok );
593 elDimension = dimension;
597 else if ( ( parseMode == Feature || parseMode == FeatureTuple ) &&
599 localNameLen ==
static_cast<int>( mGeometryAttributeUTF8Len ) &&
600 memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
602 mParseModeStack.push( QgsGmlStreamingParser::Geometry );
603 mFoundUnhandledGeometryElement =
false;
604 mGeometryString.clear();
609 mParseModeStack.push( QgsGmlStreamingParser::BoundingBox );
611 mBoundedByNullFound =
false;
613 else if ( parseMode == BoundingBox &&
616 mParseModeStack.push( QgsGmlStreamingParser::Null );
617 mBoundedByNullFound =
true;
619 else if ( parseMode == BoundingBox &&
623 mParseModeStack.push( QgsGmlStreamingParser::Envelope );
625 else if ( parseMode == Envelope &&
628 mParseModeStack.push( QgsGmlStreamingParser::LowerCorner );
631 else if ( parseMode == Envelope &&
634 mParseModeStack.push( QgsGmlStreamingParser::UpperCorner );
637 else if ( parseMode == None && !mTypeNamePtr &&
640 Q_ASSERT( !mCurrentFeature );
641 mCurrentFeature =
new QgsFeature( mFeatureCount );
643 const QgsAttributes attributes( mThematicAttributes.size() );
645 mParseModeStack.push( QgsGmlStreamingParser::Tuple );
646 mCurrentFeatureId.clear();
648 else if ( parseMode == Tuple )
650 const QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) );
651 const QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename );
652 if ( iter != mMapTypeNameToProperties.constEnd() )
654 mFeatureTupleDepth = mParseDepth;
655 mCurrentTypename = currentTypename;
656 mGeometryAttribute.clear();
657 if ( mCurrentWKB.
size() == 0 )
659 mGeometryAttribute = iter.value().mGeometryAttribute;
661 mGeometryAttributeBA = mGeometryAttribute.toUtf8();
662 mGeometryAttributePtr = mGeometryAttributeBA.constData();
663 mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
664 mParseModeStack.push( QgsGmlStreamingParser::FeatureTuple );
666 if ( mGMLNameSpaceURI.isEmpty() )
668 id = readAttribute( QString(
GML_NAMESPACE ) + NS_SEPARATOR +
"id", attr );
676 id = readAttribute( QString(
GML32_NAMESPACE ) + NS_SEPARATOR +
"id", attr );
685 id = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR +
"id", attr );
686 if ( !mCurrentFeatureId.isEmpty() )
687 mCurrentFeatureId +=
'|';
688 mCurrentFeatureId += id;
691 else if ( parseMode == None &&
692 localNameLen ==
static_cast<int>( mTypeNameUTF8Len ) &&
693 memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 )
695 Q_ASSERT( !mCurrentFeature );
696 mCurrentFeature =
new QgsFeature( mFeatureCount );
698 const QgsAttributes attributes( mThematicAttributes.size() );
700 mParseModeStack.push( QgsGmlStreamingParser::Feature );
701 mCurrentFeatureId = readAttribute( QStringLiteral(
"fid" ), attr );
702 if ( mCurrentFeatureId.isEmpty() )
707 if ( mGMLNameSpaceURI.isEmpty() )
709 mCurrentFeatureId = readAttribute( QString(
GML_NAMESPACE ) + NS_SEPARATOR +
"id", attr );
710 if ( !mCurrentFeatureId.isEmpty() )
717 mCurrentFeatureId = readAttribute( QString(
GML32_NAMESPACE ) + NS_SEPARATOR +
"id", attr );
718 if ( !mCurrentFeatureId.isEmpty() )
726 mCurrentFeatureId = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR +
"id", attr );
730 else if ( parseMode == BoundingBox && isGMLNS &&
LOCALNAME_EQUALS(
"Box" ) )
743 localNameLen ==
static_cast<int>( strlen(
"Polygon" ) ) && memcmp( pszLocalName,
"Polygon", localNameLen ) == 0 )
746 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
751 mParseModeStack.push( QgsGmlStreamingParser::MultiPoint );
753 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
758 mParseModeStack.push( QgsGmlStreamingParser::MultiLine );
760 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
765 mParseModeStack.push( QgsGmlStreamingParser::MultiPolygon );
767 else if ( parseMode == FeatureTuple )
769 const QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
770 if ( mThematicAttributes.contains( mCurrentTypename +
'|' + localName ) )
772 mParseModeStack.push( QgsGmlStreamingParser::AttributeTuple );
773 mAttributeName = mCurrentTypename +
'|' + localName;
777 else if ( parseMode == Feature )
779 const QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
780 if ( mThematicAttributes.contains( localName ) )
782 mParseModeStack.push( QgsGmlStreamingParser::Attribute );
783 mAttributeName = localName;
790 if ( localName.compare( QLatin1String(
"attribute" ), Qt::CaseInsensitive ) == 0 )
792 const QString name = readAttribute( QStringLiteral(
"name" ), attr );
793 if ( mThematicAttributes.contains( name ) )
795 const QString value = readAttribute( QStringLiteral(
"value" ), attr );
796 setAttribute( name, value );
803 QString
numberReturned = readAttribute( QStringLiteral(
"numberReturned" ), attr );
805 numberReturned = readAttribute( QStringLiteral(
"numberOfFeatures" ), attr );
809 mNumberReturned = -1;
811 const QString
numberMatched = readAttribute( QStringLiteral(
"numberMatched" ), attr );
819 mParseModeStack.push( QgsGmlStreamingParser::ExceptionReport );
824 mParseModeStack.push( QgsGmlStreamingParser::ExceptionText );
829 mTruncatedResponse =
true;
831 else if ( !mGeometryString.empty() &&
847 mFoundUnhandledGeometryElement =
true;
850 if ( !mGeometryString.empty() )
853 if ( elDimension == 0 && isGeom )
857 const QString srsDimension = readAttribute( QStringLiteral(
"srsDimension" ), attr );
859 const int dimension = srsDimension.toInt( &ok );
862 elDimension = dimension;
866 if ( elDimension != 0 || mDimensionStack.isEmpty() )
868 mDimensionStack.push( elDimension );
872 mDimensionStack.push( mDimensionStack.back() );
875 if ( mEpsg == 0 && isGeom )
877 if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
879 QgsDebugError( QStringLiteral(
"error, could not get epsg id" ) );
890void QgsGmlStreamingParser::endElement(
const XML_Char *el )
894 const int elLen =
static_cast<int>( strlen( el ) );
895 const char *pszSep = strchr( el, NS_SEPARATOR );
896 const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
897 const int nsLen = ( pszSep ) ? (
int )( pszSep - el ) : 0;
898 const int localNameLen = ( pszSep ) ? (
int )( elLen - nsLen ) - 1 : elLen;
899 const ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
901 const int lastDimension = mDimensionStack.isEmpty() ? 0 : mDimensionStack.pop();
903 const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
905 if ( parseMode == Coordinate && isGMLNS &&
LOCALNAME_EQUALS(
"coordinates" ) )
907 mParseModeStack.pop();
909 else if ( parseMode == PosList && isGMLNS &&
912 mDimension = lastDimension;
913 mParseModeStack.pop();
915 else if ( parseMode == AttributeTuple &&
916 mCurrentTypename +
'|' + QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName )
918 mParseModeStack.pop();
920 setAttribute( mAttributeName, mStringCash );
922 else if ( parseMode == Attribute && QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName )
924 mParseModeStack.pop();
926 setAttribute( mAttributeName, mStringCash );
928 else if ( parseMode == Geometry &&
929 localNameLen ==
static_cast<int>( mGeometryAttributeUTF8Len ) &&
930 memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
932 mParseModeStack.pop();
933 if ( mFoundUnhandledGeometryElement )
939 const int wkbSize = OGR_G_WkbSize( hGeom.get() );
940 unsigned char *pabyBuffer =
new unsigned char[ wkbSize ];
941 OGR_G_ExportToIsoWkb( hGeom.get(), wkbNDR, pabyBuffer );
943 g.
fromWkb( pabyBuffer, wkbSize );
944 if ( mInvertAxisOrientation )
946 g.
transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
948 Q_ASSERT( mCurrentFeature );
952 mGeometryString.clear();
954 else if ( parseMode == BoundingBox && isGMLNS &&
LOCALNAME_EQUALS(
"boundedBy" ) )
957 if ( mCurrentExtent.
isNull() &&
958 !mBoundedByNullFound &&
959 !createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) )
961 QgsDebugError( QStringLiteral(
"creation of bounding box failed" ) );
963 if ( !mCurrentExtent.
isNull() && mLayerExtent.
isNull() &&
964 !mCurrentFeature && mFeatureCount == 0 )
966 mLayerExtent = mCurrentExtent;
970 mParseModeStack.pop();
974 mParseModeStack.pop();
976 else if ( parseMode == Envelope && isGMLNS &&
LOCALNAME_EQUALS(
"Envelope" ) )
978 mParseModeStack.pop();
980 else if ( parseMode == LowerCorner && isGMLNS &&
LOCALNAME_EQUALS(
"lowerCorner" ) )
982 QList<QgsPointXY> points;
983 pointsFromPosListString( points, mStringCash, 2 );
984 if ( points.size() == 1 )
989 mParseModeStack.pop();
991 else if ( parseMode == UpperCorner && isGMLNS &&
LOCALNAME_EQUALS(
"upperCorner" ) )
993 QList<QgsPointXY> points;
994 pointsFromPosListString( points, mStringCash, 2 );
995 if ( points.size() == 1 )
1000 mParseModeStack.pop();
1002 else if ( parseMode == FeatureTuple && mParseDepth == mFeatureTupleDepth )
1004 mParseModeStack.pop();
1005 mFeatureTupleDepth = 0;
1007 else if ( ( parseMode == Tuple && !mTypeNamePtr &&
1009 ( parseMode == Feature &&
1010 localNameLen ==
static_cast<int>( mTypeNameUTF8Len ) &&
1011 memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 ) )
1013 Q_ASSERT( mCurrentFeature );
1016 if ( mCurrentWKB.
size() > 0 )
1023 else if ( !mCurrentExtent.
isEmpty() )
1032 mCurrentFeature =
nullptr;
1034 mParseModeStack.pop();
1038 QList<QgsPointXY> pointList;
1039 if ( pointsFromString( pointList, mStringCash ) != 0 )
1044 if ( pointList.isEmpty() )
1047 if ( parseMode == QgsGmlStreamingParser::Geometry )
1050 if ( getPointWKB( mCurrentWKB, *( pointList.constBegin() ) ) != 0 )
1063 if ( getPointWKB( wkbPtr, *( pointList.constBegin() ) ) != 0 )
1067 if ( !mCurrentWKBFragments.isEmpty() )
1069 mCurrentWKBFragments.last().push_back( wkbPtr );
1082 QList<QgsPointXY> pointList;
1083 if ( pointsFromString( pointList, mStringCash ) != 0 )
1087 if ( parseMode == QgsGmlStreamingParser::Geometry )
1089 if ( getLineWKB( mCurrentWKB, pointList ) != 0 )
1102 if ( getLineWKB( wkbPtr, pointList ) != 0 )
1106 if ( !mCurrentWKBFragments.isEmpty() )
1108 mCurrentWKBFragments.last().push_back( wkbPtr );
1117 else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) &&
1120 QList<QgsPointXY> pointList;
1121 if ( pointsFromString( pointList, mStringCash ) != 0 )
1127 if ( getRingWKB( wkbPtr, pointList ) != 0 )
1132 if ( !mCurrentWKBFragments.isEmpty() )
1134 mCurrentWKBFragments.last().push_back( wkbPtr );
1142 else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) && isGMLNS &&
1150 if ( parseMode == Geometry )
1152 createPolygonFromFragments();
1155 else if ( parseMode == MultiPoint && isGMLNS &&
1159 mParseModeStack.pop();
1160 createMultiPointFromFragments();
1162 else if ( parseMode == MultiLine && isGMLNS &&
1166 mParseModeStack.pop();
1167 createMultiLineFromFragments();
1169 else if ( parseMode == MultiPolygon && isGMLNS &&
1173 mParseModeStack.pop();
1174 createMultiPolygonFromFragments();
1178 mParseModeStack.pop();
1180 else if ( parseMode == ExceptionText &&
LOCALNAME_EQUALS(
"ExceptionText" ) )
1182 mExceptionText = mStringCash;
1183 mParseModeStack.pop();
1186 if ( !mGeometryString.empty() )
1188 mGeometryString.append(
"</", 2 );
1189 mGeometryString.append( pszLocalName, localNameLen );
1190 mGeometryString.append(
">", 1 );
1195void QgsGmlStreamingParser::characters(
const XML_Char *chars,
int len )
1198 if ( mParseModeStack.isEmpty() )
1203 if ( !mGeometryString.empty() )
1205 mGeometryString.append( chars, len );
1208 const QgsGmlStreamingParser::ParseMode parseMode = mParseModeStack.top();
1209 if ( parseMode == QgsGmlStreamingParser::Attribute ||
1210 parseMode == QgsGmlStreamingParser::AttributeTuple ||
1211 parseMode == QgsGmlStreamingParser::Coordinate ||
1212 parseMode == QgsGmlStreamingParser::PosList ||
1213 parseMode == QgsGmlStreamingParser::LowerCorner ||
1214 parseMode == QgsGmlStreamingParser::UpperCorner ||
1215 parseMode == QgsGmlStreamingParser::ExceptionText )
1217 mStringCash.append( QString::fromUtf8( chars, len ) );
1221void QgsGmlStreamingParser::setAttribute(
const QString &name,
const QString &value )
1224 const QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.constFind( name );
1225 bool conversionOk =
true;
1226 if ( att_it != mThematicAttributes.constEnd() )
1229 switch ( att_it.value().second.type() )
1231 case QVariant::Double:
1232 var = QVariant( value.toDouble( &conversionOk ) );
1235 var = QVariant( value.toInt( &conversionOk ) );
1237 case QVariant::LongLong:
1238 var = QVariant( value.toLongLong( &conversionOk ) );
1240 case QVariant::DateTime:
1241 var = QVariant( QDateTime::fromString( value, Qt::ISODate ) );
1244 var = QVariant( value );
1247 if ( ! conversionOk )
1251 Q_ASSERT( mCurrentFeature );
1252 mCurrentFeature->
setAttribute( att_it.value().first, var );
1256int QgsGmlStreamingParser::readEpsgFromAttribute(
int &epsgNr,
const XML_Char **attr )
1261 if ( strcmp( attr[i],
"srsName" ) == 0 )
1263 const QString
srsName( attr[i + 1] );
1275 const int eNr = code.toInt( &conversionOk );
1276 if ( !conversionOk )
1289 mInvertAxisOrientation = !mInvertAxisOrientationRequest;
1300QString QgsGmlStreamingParser::readAttribute(
const QString &attributeName,
const XML_Char **attr )
const
1305 if ( attributeName.compare( attr[i] ) == 0 )
1307 return QString::fromUtf8( attr[i + 1] );
1314bool QgsGmlStreamingParser::createBBoxFromCoordinateString(
QgsRectangle &r,
const QString &coordString )
const
1316 QList<QgsPointXY> points;
1317 if ( pointsFromCoordinateString( points, coordString ) != 0 )
1322 if ( points.size() < 2 )
1327 r.
set( points[0], points[1] );
1332int QgsGmlStreamingParser::pointsFromCoordinateString( QList<QgsPointXY> &points,
const QString &coordString )
const
1335#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1336 QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
1338 const QStringList tuples = coordString.split( mTupleSeparator, Qt::SkipEmptyParts );
1340 QStringList tuples_coordinates;
1342 bool conversionSuccess;
1344 QStringList::const_iterator tupleIterator;
1345 for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
1347#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1348 tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
1350 tuples_coordinates = tupleIterator->split( mCoordinateSeparator, Qt::SkipEmptyParts );
1352 if ( tuples_coordinates.size() < 2 )
1356 x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
1357 if ( !conversionSuccess )
1361 y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
1362 if ( !conversionSuccess )
1371int QgsGmlStreamingParser::pointsFromPosListString( QList<QgsPointXY> &points,
const QString &coordString,
int dimension )
const
1374#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1375 QStringList coordinates = coordString.split(
' ', QString::SkipEmptyParts );
1377 const QStringList coordinates = coordString.split(
' ', Qt::SkipEmptyParts );
1380 if ( coordinates.size() % dimension != 0 )
1382 QgsDebugError( QStringLiteral(
"Wrong number of coordinates" ) );
1385 const int ncoor = coordinates.size() / dimension;
1386 for (
int i = 0; i < ncoor; i++ )
1388 bool conversionSuccess;
1389 const double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
1390 if ( !conversionSuccess )
1394 const double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
1395 if ( !conversionSuccess )
1404int QgsGmlStreamingParser::pointsFromString( QList<QgsPointXY> &points,
const QString &coordString )
const
1406 if ( mCoorMode == QgsGmlStreamingParser::Coordinate )
1408 return pointsFromCoordinateString( points, coordString );
1410 else if ( mCoorMode == QgsGmlStreamingParser::PosList )
1412 return pointsFromPosListString( points, coordString, mDimension ? mDimension : 2 );
1419 const int wkbSize = 1 +
sizeof( int ) + 2 *
sizeof(
double );
1420 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1428int QgsGmlStreamingParser::getLineWKB(
QgsWkbPtr &wkbPtr,
const QList<QgsPointXY> &lineCoordinates )
const
1430 const int wkbSize = 1 + 2 *
sizeof( int ) + lineCoordinates.size() * 2 *
sizeof( double );
1431 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1437 QList<QgsPointXY>::const_iterator iter;
1438 for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
1440 fillPtr << iter->x() << iter->y();
1446int QgsGmlStreamingParser::getRingWKB(
QgsWkbPtr &wkbPtr,
const QList<QgsPointXY> &ringCoordinates )
const
1448 const int wkbSize =
sizeof( int ) + ringCoordinates.size() * 2 *
sizeof( double );
1449 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1453 fillPtr << ringCoordinates.size();
1455 QList<QgsPointXY>::const_iterator iter;
1456 for ( iter = ringCoordinates.constBegin(); iter != ringCoordinates.constEnd(); ++iter )
1458 fillPtr << iter->x() << iter->y();
1464int QgsGmlStreamingParser::createMultiLineFromFragments()
1466 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1467 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1474 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1475 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1477 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1478 wkbPtr += wkbIt->
size();
1482 mCurrentWKBFragments.clear();
1487int QgsGmlStreamingParser::createMultiPointFromFragments()
1489 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1490 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1495 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1496 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1498 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1499 wkbPtr += wkbIt->
size();
1503 mCurrentWKBFragments.clear();
1509int QgsGmlStreamingParser::createPolygonFromFragments()
1511 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1512 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1517 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1518 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1520 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1521 wkbPtr += wkbIt->
size();
1525 mCurrentWKBFragments.clear();
1530int QgsGmlStreamingParser::createMultiPolygonFromFragments()
1533 size += 1 + 2 *
sizeof( int );
1534 size += totalWKBFragmentSize();
1535 size += mCurrentWKBFragments.size() * ( 1 + 2 *
sizeof( int ) );
1537 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1543 QList< QList<QgsWkbPtr> >::const_iterator outerWkbIt = mCurrentWKBFragments.constBegin();
1545 for ( ; outerWkbIt != mCurrentWKBFragments.constEnd(); ++outerWkbIt )
1550 QList<QgsWkbPtr>::const_iterator innerWkbIt = outerWkbIt->constBegin();
1551 for ( ; innerWkbIt != outerWkbIt->constEnd(); ++innerWkbIt )
1553 memcpy( wkbPtr, *innerWkbIt, innerWkbIt->size() );
1554 wkbPtr += innerWkbIt->
size();
1555 delete[] *innerWkbIt;
1559 mCurrentWKBFragments.clear();
1564int QgsGmlStreamingParser::totalWKBFragmentSize()
const
1567 const auto constMCurrentWKBFragments = mCurrentWKBFragments;
1568 for (
const QList<QgsWkbPtr> &list : constMCurrentWKBFragments )
1570 const auto constList = list;
1579void QgsGmlStreamingParser::createParser(
const QByteArray &encoding )
1581 Q_ASSERT( !mParser );
1583 mParser = XML_ParserCreateNS( encoding.isEmpty() ?
nullptr : encoding.data(), NS_SEPARATOR );
1584 XML_SetUserData( mParser,
this );
1585 XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
1586 XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
The Qgis class provides global constants for use throughout the application.
@ Critical
Critical/error message.
WkbType
The WKB type describes the number of dimensions a geometry has.
@ MultiPolygon
MultiPolygon.
@ MultiLineString
MultiLineString.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static endian_t endian()
Returns whether this machine uses big or little endian.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
void setValid(bool validity)
Sets the validity of the feature.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Container of fields for a vector layer.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QPair< QgsFeature *, QString > QgsGmlFeaturePtrGmlIdPair
int numberReturned() const
Returns WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found.
Qgis::WkbType wkbType() const
Returns the geometry type.
AxisOrientationLogic
Axis orientation logic.
@ Honour_EPSG
Honour EPSG axis order.
@ Honour_EPSG_if_urn
Honour EPSG axis order only if srsName is of the form urn:ogc:def:crs:EPSG:
int numberMatched() const
Returns WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found.
QgsGmlStreamingParser(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields, AxisOrientationLogic axisOrientationLogic=Honour_EPSG_if_urn, bool invertAxisOrientation=false)
Constructor.
bool processData(const QByteArray &data, bool atEnd, QString &errorMsg)
Process a new chunk of data.
int getEPSGCode() const
Returns the EPSG code, or 0 if unknown.
QVector< QgsGmlFeaturePtrGmlIdPair > getAndStealReadyFeatures()
Returns the list of features that have been completely parsed.
QString srsName() const
Returns the value of the srsName attribute.
void totalStepsUpdate(int totalSteps)
void dataReadProgress(int progress)
QgsGml(const QString &typeName, const QString &geometryAttribute, const QgsFields &fields)
QgsCoordinateReferenceSystem crs() const
Returns features spatial reference system.
int getFeatures(const QString &uri, Qgis::WkbType *wkbType, QgsRectangle *extent=nullptr, const QString &userName=QString(), const QString &password=QString(), const QString &authcfg=QString())
Does the Http GET request to the wfs server.
void dataProgressAndSteps(int progress, int totalSteps)
Also emit signal with progress and totalSteps together (this is better for the status message)
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
@ OGC_HTTP_URI
e.g. urn:x-ogc:def:crs:EPSG::4326
@ X_OGC_URN
e.g. urn:ogc:def:crs:EPSG::4326
@ OGC_URN
e.g. http://www.opengis.net/gml/srs/epsg.xml#4326 (called "OGC HTTP URL" in GeoServer WFS configurati...
static CRSFlavor parseCrsName(const QString &crsName, QString &authority, QString &code)
Parse a CRS name in one of the flavors of OGC services, and decompose it as authority and code.
A class to represent a 2D point.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
bool isNull() const
Test if the rectangle is null (holding no spatial information).
void set(const QgsPointXY &p1, const QgsPointXY &p2, bool normalize=true)
Sets the rectangle from two QgsPoints.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool isEmpty() const
Returns true if the rectangle has no area.
void setNull()
Mark a rectangle as being null (holding no spatial information).
std::unique_ptr< std::remove_pointer< OGRGeometryH >::type, OGRGeometryDeleter > ogr_geometry_unique_ptr
Scoped OGR geometry.
#define LOCALNAME_EQUALS(string_constant)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)
const QgsCoordinateReferenceSystem & crs