28 #include <QNetworkRequest>
29 #include <QNetworkReply>
30 #include <QProgressDialog>
35 #include <QRegularExpression>
42 static const char *
GML_NAMESPACE =
"http://www.opengis.net/gml";
47 const QString &geometryAttribute,
49 : mParser(
typeName, geometryAttribute, fields )
53 const int index = mTypeName.indexOf(
':' );
54 if ( index != -1 && index < mTypeName.length() )
56 mTypeName = mTypeName.mid( index + 1 );
65 QNetworkRequest request( uri );
68 if ( !authcfg.isEmpty() )
73 tr(
"GML Getfeature network request update failed for authcfg %1" ).arg( authcfg ),
75 Qgis::MessageLevel::Critical
80 else if ( !userName.isNull() || !password.isNull() )
82 request.setRawHeader(
"Authorization",
"Basic " + QStringLiteral(
"%1:%2" ).arg( userName, password ).toLatin1().toBase64() );
86 if ( !authcfg.isEmpty() )
92 tr(
"GML Getfeature network reply update failed for authcfg %1" ).arg( authcfg ),
94 Qgis::MessageLevel::Critical
100 connect( reply, &QNetworkReply::finished,
this, &QgsGml::setFinished );
101 connect( reply, &QNetworkReply::downloadProgress,
this, &QgsGml::handleProgressEvent );
104 QProgressDialog *progressDialog =
nullptr;
105 QWidget *mainWindow =
nullptr;
106 const QWidgetList topLevelWidgets = qApp->topLevelWidgets();
107 for ( QWidgetList::const_iterator it = topLevelWidgets.constBegin(); it != topLevelWidgets.constEnd(); ++it )
109 if ( ( *it )->objectName() == QLatin1String(
"QgisApp" ) )
117 progressDialog =
new QProgressDialog( tr(
"Loading GML data\n%1" ).arg( mTypeName ), tr(
"Abort" ), 0, 0, mainWindow );
118 progressDialog->setWindowModality( Qt::ApplicationModal );
121 connect( progressDialog, &QProgressDialog::canceled,
this, &QgsGml::setFinished );
122 progressDialog->show();
132 const QByteArray readData = reply->readAll();
133 if ( !readData.isEmpty() )
136 if ( !mParser.
processData( readData, atEnd, errorMsg ) )
140 QCoreApplication::processEvents();
143 fillMapsFromParser();
145 const QNetworkReply::NetworkError replyError = reply->error();
146 const QString replyErrorString = reply->errorString();
149 delete progressDialog;
154 tr(
"GML Getfeature network request failed with error: %1" ).arg( replyErrorString ),
156 Qgis::MessageLevel::Critical
168 calculateExtentFromFeatures();
183 if ( !mParser.
processData( data,
true , errorMsg ) )
186 fillMapsFromParser();
196 void QgsGml::fillMapsFromParser()
199 const auto constFeatures = features;
203 const QString &gmlId = featPair.second;
204 mFeatures.insert( feat->
id(), feat );
205 if ( !gmlId.isEmpty() )
207 mIdMap.insert( feat->
id(), gmlId );
212 void QgsGml::setFinished()
217 void QgsGml::handleProgressEvent( qint64 progress, qint64 totalSteps )
219 if ( totalSteps < 0 )
229 void QgsGml::calculateExtentFromFeatures()
231 if ( mFeatures.empty() )
238 bool bboxInitialized =
false;
240 for (
int i = 0; i < mFeatures.size(); ++i )
242 currentFeature = mFeatures[i];
243 if ( !currentFeature )
247 currentGeometry = currentFeature->
geometry();
248 if ( !currentGeometry.
isNull() )
250 if ( !bboxInitialized )
253 bboxInitialized =
true;
278 const QString &geometryAttribute,
281 bool invertAxisOrientation )
283 , mTypeNameBA( mTypeName.toUtf8() )
284 , mTypeNamePtr( mTypeNameBA.constData() )
285 , mTypeNameUTF8Len( strlen( mTypeNamePtr ) )
287 , mGeometryAttribute( geometryAttribute )
288 , mGeometryAttributeBA( geometryAttribute.toUtf8() )
289 , mGeometryAttributePtr( mGeometryAttributeBA.constData() )
290 , mGeometryAttributeUTF8Len( strlen( mGeometryAttributePtr ) )
292 , mIsException( false )
293 , mTruncatedResponse( false )
295 , mFeatureTupleDepth( 0 )
297 , mCurrentWKB( nullptr, 0 )
298 , mBoundedByNullFound( false )
300 , mCoorMode( Coordinate )
302 , mAxisOrientationLogic( axisOrientationLogic )
303 , mInvertAxisOrientationRequest( invertAxisOrientation )
304 , mInvertAxisOrientation( invertAxisOrientation )
305 , mNumberReturned( -1 )
306 , mNumberMatched( -1 )
307 , mFoundUnhandledGeometryElement( false )
309 mThematicAttributes.clear();
310 for (
int i = 0; i < fields.
size(); i++ )
312 mThematicAttributes.insert( fields.
at( i ).
name(), qMakePair( i, fields.
at( i ) ) );
317 const int index = mTypeName.indexOf(
':' );
318 if ( index != -1 && index < mTypeName.length() )
320 mTypeName = mTypeName.mid( index + 1 );
321 mTypeNameBA = mTypeName.toUtf8();
322 mTypeNamePtr = mTypeNameBA.constData();
323 mTypeNameUTF8Len = strlen( mTypeNamePtr );
329 static QString stripNS(
const QString &
string )
331 const int index =
string.indexOf(
':' );
332 if ( index != -1 && index <
string.length() )
334 return string.mid( index + 1 );
341 const QMap< QString, QPair<QString, QString> > &mapFieldNameToSrcLayerNameFieldName,
343 bool invertAxisOrientation )
344 : mLayerProperties( layerProperties )
345 , mTypeNameUTF8Len( 0 )
347 , mGeometryAttributeUTF8Len( 0 )
349 , mIsException( false )
350 , mTruncatedResponse( false )
352 , mFeatureTupleDepth( 0 )
354 , mCurrentWKB( nullptr, 0 )
355 , mBoundedByNullFound( false )
357 , mCoorMode( Coordinate )
359 , mAxisOrientationLogic( axisOrientationLogic )
360 , mInvertAxisOrientationRequest( invertAxisOrientation )
361 , mInvertAxisOrientation( invertAxisOrientation )
362 , mNumberReturned( -1 )
363 , mNumberMatched( -1 )
364 , mFoundUnhandledGeometryElement( false )
366 mThematicAttributes.clear();
367 for (
int i = 0; i < fields.
size(); i++ )
369 const QMap< QString, QPair<QString, QString> >::const_iterator att_it = mapFieldNameToSrcLayerNameFieldName.constFind( fields.
at( i ).
name() );
370 if ( att_it != mapFieldNameToSrcLayerNameFieldName.constEnd() )
372 if ( mLayerProperties.size() == 1 )
373 mThematicAttributes.insert( att_it.value().second, qMakePair( i, fields.
at( i ) ) );
375 mThematicAttributes.insert( stripNS( att_it.value().first ) +
"|" + att_it.value().second, qMakePair( i, fields.
at( i ) ) );
378 bool alreadyFoundGeometry =
false;
379 for (
int i = 0; i < mLayerProperties.size(); i++ )
382 if ( !mLayerProperties[i].mGeometryAttribute.isEmpty() )
384 if ( alreadyFoundGeometry )
386 QgsDebugMsgLevel( QStringLiteral(
"Will ignore geometry field %1 from typename %2" ).
387 arg( mLayerProperties[i].mGeometryAttribute, mLayerProperties[i].mName ), 2 );
388 mLayerProperties[i].mGeometryAttribute.clear();
390 alreadyFoundGeometry =
true;
392 mMapTypeNameToProperties.insert( stripNS( mLayerProperties[i].mName ), mLayerProperties[i] );
395 if ( mLayerProperties.size() == 1 )
397 mTypeName = mLayerProperties[0].mName;
398 mGeometryAttribute = mLayerProperties[0].mGeometryAttribute;
399 mGeometryAttributeBA = mGeometryAttribute.toUtf8();
400 mGeometryAttributePtr = mGeometryAttributeBA.constData();
401 mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
402 const int index = mTypeName.indexOf(
':' );
403 if ( index != -1 && index < mTypeName.length() )
405 mTypeName = mTypeName.mid( index + 1 );
407 mTypeNameBA = mTypeName.toUtf8();
408 mTypeNamePtr = mTypeNameBA.constData();
409 mTypeNameUTF8Len = strlen( mTypeNamePtr );
420 XML_ParserFree( mParser );
423 const auto constMFeatureList = mFeatureList;
426 delete featPair.first;
429 delete mCurrentFeature;
445 QByteArray data = pdata;
450 QString strData = mCodec->toUnicode( pdata );
451 data = strData.toUtf8();
454 if ( XML_Parse( mParser, data, data.size(), atEnd ) == XML_STATUS_ERROR )
456 const XML_Error errorCode = XML_GetErrorCode( mParser );
457 if ( !mCodec && errorCode == XML_ERROR_UNKNOWN_ENCODING )
461 QRegularExpression reEncoding( QStringLiteral(
"<?xml.*encoding=['\"]([^'\"]*)['\"].*?>" ),
462 QRegularExpression::CaseInsensitiveOption );
463 QRegularExpressionMatch match = reEncoding.match( pdata );
464 const QString encoding = match.hasMatch() ? match.captured( 1 ) : QString();
465 mCodec = !encoding.isEmpty() ? QTextCodec::codecForName( encoding.toLatin1() ) :
nullptr;
469 XML_ParserFree( mParser );
471 createParser( QByteArrayLiteral(
"UTF-8" ) );
477 errorMsg = QObject::tr(
"Error: %1 on line %2, column %3" )
478 .arg( XML_ErrorString( errorCode ) )
479 .arg( XML_GetCurrentLineNumber( mParser ) )
480 .arg( XML_GetCurrentColumnNumber( mParser ) );
490 QVector<QgsGmlFeaturePtrGmlIdPair> ret = mFeatureList;
491 mFeatureList.clear();
495 #define LOCALNAME_EQUALS(string_constant) \
496 ( localNameLen == static_cast<int>(strlen( string_constant )) && memcmp(pszLocalName, string_constant, localNameLen) == 0 )
498 void QgsGmlStreamingParser::startElement(
const XML_Char *el,
const XML_Char **attr )
500 const int elLen =
static_cast<int>( strlen( el ) );
502 const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
503 const int nsLen = ( pszSep ) ? (
int )( pszSep - el ) : 0;
504 const int localNameLen = ( pszSep ) ? (
int )( elLen - nsLen ) - 1 : elLen;
505 const ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
509 if ( !mGMLNameSpaceURIPtr && pszSep )
523 const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
526 if ( parseMode == Geometry || parseMode == Coordinate || parseMode == PosList ||
527 parseMode == MultiPoint || parseMode == MultiLine || parseMode == MultiPolygon )
529 mGeometryString.append(
"<", 1 );
530 mGeometryString.append( pszLocalName, localNameLen );
531 mGeometryString.append(
" ", 1 );
532 for (
const XML_Char **attrIter = attr; attrIter && *attrIter; attrIter += 2 )
534 const size_t nAttrLen = strlen( attrIter[0] );
537 if ( nAttrLen > GML32_NAMESPACE_LEN &&
538 attrIter[0][GML32_NAMESPACE_LEN] ==
'?' &&
541 mGeometryString.append(
"gml:" );
542 mGeometryString.append( attrIter[0] + GML32_NAMESPACE_LEN + 1 );
544 else if ( nAttrLen > GML_NAMESPACE_LEN &&
545 attrIter[0][GML_NAMESPACE_LEN] ==
'?' &&
546 memcmp( attrIter[0],
GML_NAMESPACE, GML_NAMESPACE_LEN ) == 0 )
548 mGeometryString.append(
"gml:" );
549 mGeometryString.append( attrIter[0] + GML_NAMESPACE_LEN + 1 );
553 mGeometryString.append( attrIter[0] );
555 mGeometryString.append(
"=\"", 2 );
556 mGeometryString.append( attrIter[1] );
557 mGeometryString.append(
"\" ", 2 );
560 mGeometryString.append(
">", 1 );
565 mParseModeStack.push( Coordinate );
566 mCoorMode = QgsGmlStreamingParser::Coordinate;
568 mCoordinateSeparator = readAttribute( QStringLiteral(
"cs" ), attr );
569 if ( mCoordinateSeparator.isEmpty() )
571 mCoordinateSeparator =
',';
573 mTupleSeparator = readAttribute( QStringLiteral(
"ts" ), attr );
574 if ( mTupleSeparator.isEmpty() )
576 mTupleSeparator =
' ';
582 mParseModeStack.push( QgsGmlStreamingParser::PosList );
583 mCoorMode = QgsGmlStreamingParser::PosList;
585 if ( elDimension == 0 )
587 const QString srsDimension = readAttribute( QStringLiteral(
"srsDimension" ), attr );
589 const int dimension = srsDimension.toInt( &ok );
592 elDimension = dimension;
596 else if ( ( parseMode == Feature || parseMode == FeatureTuple ) &&
598 localNameLen ==
static_cast<int>( mGeometryAttributeUTF8Len ) &&
599 memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
601 mParseModeStack.push( QgsGmlStreamingParser::Geometry );
602 mFoundUnhandledGeometryElement =
false;
603 mGeometryString.clear();
608 mParseModeStack.push( QgsGmlStreamingParser::BoundingBox );
610 mBoundedByNullFound =
false;
612 else if ( parseMode == BoundingBox &&
615 mParseModeStack.push( QgsGmlStreamingParser::Null );
616 mBoundedByNullFound =
true;
618 else if ( parseMode == BoundingBox &&
622 mParseModeStack.push( QgsGmlStreamingParser::Envelope );
624 else if ( parseMode == Envelope &&
627 mParseModeStack.push( QgsGmlStreamingParser::LowerCorner );
630 else if ( parseMode == Envelope &&
633 mParseModeStack.push( QgsGmlStreamingParser::UpperCorner );
636 else if ( parseMode == None && !mTypeNamePtr &&
639 Q_ASSERT( !mCurrentFeature );
640 mCurrentFeature =
new QgsFeature( mFeatureCount );
642 const QgsAttributes attributes( mThematicAttributes.size() );
644 mParseModeStack.push( QgsGmlStreamingParser::Tuple );
645 mCurrentFeatureId.clear();
647 else if ( parseMode == Tuple )
649 const QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) );
650 const QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename );
651 if ( iter != mMapTypeNameToProperties.constEnd() )
653 mFeatureTupleDepth = mParseDepth;
654 mCurrentTypename = currentTypename;
655 mGeometryAttribute.clear();
656 if ( mCurrentWKB.
size() == 0 )
658 mGeometryAttribute = iter.value().mGeometryAttribute;
660 mGeometryAttributeBA = mGeometryAttribute.toUtf8();
661 mGeometryAttributePtr = mGeometryAttributeBA.constData();
662 mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
663 mParseModeStack.push( QgsGmlStreamingParser::FeatureTuple );
665 if ( mGMLNameSpaceURI.isEmpty() )
684 id = readAttribute( mGMLNameSpaceURI +
NS_SEPARATOR +
"id", attr );
685 if ( !mCurrentFeatureId.isEmpty() )
686 mCurrentFeatureId +=
'|';
687 mCurrentFeatureId += id;
690 else if ( parseMode == None &&
691 localNameLen ==
static_cast<int>( mTypeNameUTF8Len ) &&
692 memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 )
694 Q_ASSERT( !mCurrentFeature );
695 mCurrentFeature =
new QgsFeature( mFeatureCount );
697 const QgsAttributes attributes( mThematicAttributes.size() );
699 mParseModeStack.push( QgsGmlStreamingParser::Feature );
700 mCurrentFeatureId = readAttribute( QStringLiteral(
"fid" ), attr );
701 if ( mCurrentFeatureId.isEmpty() )
706 if ( mGMLNameSpaceURI.isEmpty() )
709 if ( !mCurrentFeatureId.isEmpty() )
717 if ( !mCurrentFeatureId.isEmpty() )
725 mCurrentFeatureId = readAttribute( mGMLNameSpaceURI +
NS_SEPARATOR +
"id", attr );
729 else if ( parseMode == BoundingBox && isGMLNS &&
LOCALNAME_EQUALS(
"Box" ) )
742 localNameLen ==
static_cast<int>( strlen(
"Polygon" ) ) && memcmp( pszLocalName,
"Polygon", localNameLen ) == 0 )
745 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
750 mParseModeStack.push( QgsGmlStreamingParser::MultiPoint );
752 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
757 mParseModeStack.push( QgsGmlStreamingParser::MultiLine );
759 mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
764 mParseModeStack.push( QgsGmlStreamingParser::MultiPolygon );
766 else if ( parseMode == FeatureTuple )
768 const QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
769 if ( mThematicAttributes.contains( mCurrentTypename +
'|' + localName ) )
771 mParseModeStack.push( QgsGmlStreamingParser::AttributeTuple );
772 mAttributeName = mCurrentTypename +
'|' + localName;
776 else if ( parseMode == Feature )
778 const QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
779 if ( mThematicAttributes.contains( localName ) )
781 mParseModeStack.push( QgsGmlStreamingParser::Attribute );
782 mAttributeName = localName;
789 if ( localName.compare( QLatin1String(
"attribute" ), Qt::CaseInsensitive ) == 0 )
791 const QString name = readAttribute( QStringLiteral(
"name" ), attr );
792 if ( mThematicAttributes.contains( name ) )
794 const QString value = readAttribute( QStringLiteral(
"value" ), attr );
795 setAttribute( name, value );
802 QString
numberReturned = readAttribute( QStringLiteral(
"numberReturned" ), attr );
804 numberReturned = readAttribute( QStringLiteral(
"numberOfFeatures" ), attr );
808 mNumberReturned = -1;
810 const QString
numberMatched = readAttribute( QStringLiteral(
"numberMatched" ), attr );
818 mParseModeStack.push( QgsGmlStreamingParser::ExceptionReport );
823 mParseModeStack.push( QgsGmlStreamingParser::ExceptionText );
828 mTruncatedResponse =
true;
830 else if ( !mGeometryString.empty() &&
846 mFoundUnhandledGeometryElement =
true;
849 if ( !mGeometryString.empty() )
852 if ( elDimension == 0 && isGeom )
856 const QString srsDimension = readAttribute( QStringLiteral(
"srsDimension" ), attr );
858 const int dimension = srsDimension.toInt( &ok );
861 elDimension = dimension;
865 if ( elDimension != 0 || mDimensionStack.isEmpty() )
867 mDimensionStack.push( elDimension );
871 mDimensionStack.push( mDimensionStack.back() );
874 if ( mEpsg == 0 && isGeom )
876 if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
878 QgsDebugMsg( QStringLiteral(
"error, could not get epsg id" ) );
889 void QgsGmlStreamingParser::endElement(
const XML_Char *el )
893 const int elLen =
static_cast<int>( strlen( el ) );
895 const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
896 const int nsLen = ( pszSep ) ? (
int )( pszSep - el ) : 0;
897 const int localNameLen = ( pszSep ) ? (
int )( elLen - nsLen ) - 1 : elLen;
898 const ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
900 const int lastDimension = mDimensionStack.isEmpty() ? 0 : mDimensionStack.pop();
902 const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
904 if ( parseMode == Coordinate && isGMLNS &&
LOCALNAME_EQUALS(
"coordinates" ) )
906 mParseModeStack.pop();
908 else if ( parseMode == PosList && isGMLNS &&
911 mDimension = lastDimension;
912 mParseModeStack.pop();
914 else if ( parseMode == AttributeTuple &&
915 mCurrentTypename +
'|' + QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName )
917 mParseModeStack.pop();
919 setAttribute( mAttributeName, mStringCash );
921 else if ( parseMode == Attribute && QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName )
923 mParseModeStack.pop();
925 setAttribute( mAttributeName, mStringCash );
927 else if ( parseMode == Geometry &&
928 localNameLen ==
static_cast<int>( mGeometryAttributeUTF8Len ) &&
929 memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
931 mParseModeStack.pop();
932 if ( mFoundUnhandledGeometryElement )
938 const int wkbSize = OGR_G_WkbSize( hGeom.get() );
939 unsigned char *pabyBuffer =
new unsigned char[ wkbSize ];
940 OGR_G_ExportToIsoWkb( hGeom.get(), wkbNDR, pabyBuffer );
942 g.
fromWkb( pabyBuffer, wkbSize );
943 if ( mInvertAxisOrientation )
945 g.
transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
947 Q_ASSERT( mCurrentFeature );
951 mGeometryString.clear();
953 else if ( parseMode == BoundingBox && isGMLNS &&
LOCALNAME_EQUALS(
"boundedBy" ) )
956 if ( mCurrentExtent.
isNull() &&
957 !mBoundedByNullFound &&
958 !createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) )
960 QgsDebugMsg( QStringLiteral(
"creation of bounding box failed" ) );
962 if ( !mCurrentExtent.
isNull() && mLayerExtent.
isNull() &&
963 !mCurrentFeature && mFeatureCount == 0 )
965 mLayerExtent = mCurrentExtent;
969 mParseModeStack.pop();
973 mParseModeStack.pop();
975 else if ( parseMode == Envelope && isGMLNS &&
LOCALNAME_EQUALS(
"Envelope" ) )
977 mParseModeStack.pop();
979 else if ( parseMode == LowerCorner && isGMLNS &&
LOCALNAME_EQUALS(
"lowerCorner" ) )
981 QList<QgsPointXY> points;
982 pointsFromPosListString( points, mStringCash, 2 );
983 if ( points.size() == 1 )
988 mParseModeStack.pop();
990 else if ( parseMode == UpperCorner && isGMLNS &&
LOCALNAME_EQUALS(
"upperCorner" ) )
992 QList<QgsPointXY> points;
993 pointsFromPosListString( points, mStringCash, 2 );
994 if ( points.size() == 1 )
999 mParseModeStack.pop();
1001 else if ( parseMode == FeatureTuple && mParseDepth == mFeatureTupleDepth )
1003 mParseModeStack.pop();
1004 mFeatureTupleDepth = 0;
1006 else if ( ( parseMode == Tuple && !mTypeNamePtr &&
1008 ( parseMode == Feature &&
1009 localNameLen ==
static_cast<int>( mTypeNameUTF8Len ) &&
1010 memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 ) )
1012 Q_ASSERT( mCurrentFeature );
1015 if ( mCurrentWKB.
size() > 0 )
1022 else if ( !mCurrentExtent.
isEmpty() )
1031 mCurrentFeature =
nullptr;
1033 mParseModeStack.pop();
1037 QList<QgsPointXY> pointList;
1038 if ( pointsFromString( pointList, mStringCash ) != 0 )
1043 if ( pointList.isEmpty() )
1046 if ( parseMode == QgsGmlStreamingParser::Geometry )
1049 if ( getPointWKB( mCurrentWKB, *( pointList.constBegin() ) ) != 0 )
1062 if ( getPointWKB( wkbPtr, *( pointList.constBegin() ) ) != 0 )
1066 if ( !mCurrentWKBFragments.isEmpty() )
1068 mCurrentWKBFragments.last().push_back( wkbPtr );
1072 QgsDebugMsg( QStringLiteral(
"No wkb fragments" ) );
1081 QList<QgsPointXY> pointList;
1082 if ( pointsFromString( pointList, mStringCash ) != 0 )
1086 if ( parseMode == QgsGmlStreamingParser::Geometry )
1088 if ( getLineWKB( mCurrentWKB, pointList ) != 0 )
1101 if ( getLineWKB( wkbPtr, pointList ) != 0 )
1105 if ( !mCurrentWKBFragments.isEmpty() )
1107 mCurrentWKBFragments.last().push_back( wkbPtr );
1111 QgsDebugMsg( QStringLiteral(
"no wkb fragments" ) );
1116 else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) &&
1119 QList<QgsPointXY> pointList;
1120 if ( pointsFromString( pointList, mStringCash ) != 0 )
1126 if ( getRingWKB( wkbPtr, pointList ) != 0 )
1131 if ( !mCurrentWKBFragments.isEmpty() )
1133 mCurrentWKBFragments.last().push_back( wkbPtr );
1138 QgsDebugMsg( QStringLiteral(
"no wkb fragments" ) );
1141 else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) && isGMLNS &&
1149 if ( parseMode == Geometry )
1151 createPolygonFromFragments();
1154 else if ( parseMode == MultiPoint && isGMLNS &&
1158 mParseModeStack.pop();
1159 createMultiPointFromFragments();
1161 else if ( parseMode == MultiLine && isGMLNS &&
1165 mParseModeStack.pop();
1166 createMultiLineFromFragments();
1168 else if ( parseMode == MultiPolygon && isGMLNS &&
1172 mParseModeStack.pop();
1173 createMultiPolygonFromFragments();
1177 mParseModeStack.pop();
1179 else if ( parseMode == ExceptionText &&
LOCALNAME_EQUALS(
"ExceptionText" ) )
1181 mExceptionText = mStringCash;
1182 mParseModeStack.pop();
1185 if ( !mGeometryString.empty() )
1187 mGeometryString.append(
"</", 2 );
1188 mGeometryString.append( pszLocalName, localNameLen );
1189 mGeometryString.append(
">", 1 );
1194 void QgsGmlStreamingParser::characters(
const XML_Char *chars,
int len )
1197 if ( mParseModeStack.isEmpty() )
1202 if ( !mGeometryString.empty() )
1204 mGeometryString.append( chars, len );
1207 const QgsGmlStreamingParser::ParseMode parseMode = mParseModeStack.top();
1208 if ( parseMode == QgsGmlStreamingParser::Attribute ||
1209 parseMode == QgsGmlStreamingParser::AttributeTuple ||
1210 parseMode == QgsGmlStreamingParser::Coordinate ||
1211 parseMode == QgsGmlStreamingParser::PosList ||
1212 parseMode == QgsGmlStreamingParser::LowerCorner ||
1213 parseMode == QgsGmlStreamingParser::UpperCorner ||
1214 parseMode == QgsGmlStreamingParser::ExceptionText )
1216 mStringCash.append( QString::fromUtf8( chars, len ) );
1220 void QgsGmlStreamingParser::setAttribute(
const QString &name,
const QString &value )
1223 const QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.constFind( name );
1224 bool conversionOk =
true;
1225 if ( att_it != mThematicAttributes.constEnd() )
1228 switch ( att_it.value().second.type() )
1230 case QVariant::Double:
1231 var = QVariant( value.toDouble( &conversionOk ) );
1234 var = QVariant( value.toInt( &conversionOk ) );
1236 case QVariant::LongLong:
1237 var = QVariant( value.toLongLong( &conversionOk ) );
1239 case QVariant::DateTime:
1240 var = QVariant( QDateTime::fromString( value, Qt::ISODate ) );
1243 var = QVariant( value );
1246 if ( ! conversionOk )
1250 Q_ASSERT( mCurrentFeature );
1251 mCurrentFeature->
setAttribute( att_it.value().first, var );
1255 int QgsGmlStreamingParser::readEpsgFromAttribute(
int &epsgNr,
const XML_Char **attr )
1260 if ( strcmp( attr[i],
"srsName" ) == 0 )
1262 const QString epsgString( attr[i + 1] );
1263 QString epsgNrString;
1264 bool bIsUrn =
false;
1265 if ( epsgString.startsWith( QLatin1String(
"http://www.opengis.net/gml/srs/" ) ) )
1267 epsgNrString = epsgString.section(
'#', 1, 1 );
1270 else if ( epsgString.startsWith( QLatin1String(
"urn:ogc:def:crs:EPSG:" ) ) ||
1271 epsgString.startsWith( QLatin1String(
"urn:x-ogc:def:crs:EPSG:" ) ) )
1274 epsgNrString = epsgString.split(
':' ).last();
1276 else if ( epsgString.startsWith( QLatin1String(
"http://www.opengis.net/def/crs/EPSG/" ) ) )
1279 epsgNrString = epsgString.split(
'/' ).last();
1283 epsgNrString = epsgString.section(
':', 1, 1 );
1286 const int eNr = epsgNrString.toInt( &conversionOk );
1287 if ( !conversionOk )
1292 mSrsName = epsgString;
1300 mInvertAxisOrientation = !mInvertAxisOrientationRequest;
1311 QString QgsGmlStreamingParser::readAttribute(
const QString &attributeName,
const XML_Char **attr )
const
1316 if ( attributeName.compare( attr[i] ) == 0 )
1318 return QString::fromUtf8( attr[i + 1] );
1325 bool QgsGmlStreamingParser::createBBoxFromCoordinateString(
QgsRectangle &r,
const QString &coordString )
const
1327 QList<QgsPointXY> points;
1328 if ( pointsFromCoordinateString( points, coordString ) != 0 )
1333 if ( points.size() < 2 )
1338 r.
set( points[0], points[1] );
1343 int QgsGmlStreamingParser::pointsFromCoordinateString( QList<QgsPointXY> &points,
const QString &coordString )
const
1346 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1347 QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
1349 const QStringList tuples = coordString.split( mTupleSeparator, Qt::SkipEmptyParts );
1351 QStringList tuples_coordinates;
1353 bool conversionSuccess;
1355 QStringList::const_iterator tupleIterator;
1356 for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
1358 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1359 tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
1361 tuples_coordinates = tupleIterator->split( mCoordinateSeparator, Qt::SkipEmptyParts );
1363 if ( tuples_coordinates.size() < 2 )
1367 x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
1368 if ( !conversionSuccess )
1372 y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
1373 if ( !conversionSuccess )
1382 int QgsGmlStreamingParser::pointsFromPosListString( QList<QgsPointXY> &points,
const QString &coordString,
int dimension )
const
1385 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1386 QStringList coordinates = coordString.split(
' ', QString::SkipEmptyParts );
1388 const QStringList coordinates = coordString.split(
' ', Qt::SkipEmptyParts );
1391 if ( coordinates.size() % dimension != 0 )
1393 QgsDebugMsg( QStringLiteral(
"Wrong number of coordinates" ) );
1396 const int ncoor = coordinates.size() / dimension;
1397 for (
int i = 0; i < ncoor; i++ )
1399 bool conversionSuccess;
1400 const double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
1401 if ( !conversionSuccess )
1405 const double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
1406 if ( !conversionSuccess )
1415 int QgsGmlStreamingParser::pointsFromString( QList<QgsPointXY> &points,
const QString &coordString )
const
1417 if ( mCoorMode == QgsGmlStreamingParser::Coordinate )
1419 return pointsFromCoordinateString( points, coordString );
1421 else if ( mCoorMode == QgsGmlStreamingParser::PosList )
1423 return pointsFromPosListString( points, coordString, mDimension ? mDimension : 2 );
1428 int QgsGmlStreamingParser::getPointWKB(
QgsWkbPtr &wkbPtr,
const QgsPointXY &point )
const
1430 const int wkbSize = 1 +
sizeof( int ) + 2 *
sizeof(
double );
1431 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1439 int QgsGmlStreamingParser::getLineWKB(
QgsWkbPtr &wkbPtr,
const QList<QgsPointXY> &lineCoordinates )
const
1441 const int wkbSize = 1 + 2 *
sizeof( int ) + lineCoordinates.size() * 2 *
sizeof( double );
1442 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1448 QList<QgsPointXY>::const_iterator iter;
1449 for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
1451 fillPtr << iter->x() << iter->y();
1457 int QgsGmlStreamingParser::getRingWKB(
QgsWkbPtr &wkbPtr,
const QList<QgsPointXY> &ringCoordinates )
const
1459 const int wkbSize =
sizeof( int ) + ringCoordinates.size() * 2 *
sizeof( double );
1460 wkbPtr =
QgsWkbPtr(
new unsigned char[wkbSize], wkbSize );
1464 fillPtr << ringCoordinates.size();
1466 QList<QgsPointXY>::const_iterator iter;
1467 for ( iter = ringCoordinates.constBegin(); iter != ringCoordinates.constEnd(); ++iter )
1469 fillPtr << iter->x() << iter->y();
1475 int QgsGmlStreamingParser::createMultiLineFromFragments()
1477 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1478 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1485 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1486 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1488 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1489 wkbPtr += wkbIt->
size();
1493 mCurrentWKBFragments.clear();
1498 int QgsGmlStreamingParser::createMultiPointFromFragments()
1500 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1501 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1506 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1507 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1509 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1510 wkbPtr += wkbIt->
size();
1514 mCurrentWKBFragments.clear();
1520 int QgsGmlStreamingParser::createPolygonFromFragments()
1522 const int size = 1 + 2 *
sizeof( int ) + totalWKBFragmentSize();
1523 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1528 QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1529 for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1531 memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1532 wkbPtr += wkbIt->
size();
1536 mCurrentWKBFragments.clear();
1541 int QgsGmlStreamingParser::createMultiPolygonFromFragments()
1544 size += 1 + 2 *
sizeof( int );
1545 size += totalWKBFragmentSize();
1546 size += mCurrentWKBFragments.size() * ( 1 + 2 *
sizeof( int ) );
1548 mCurrentWKB =
QgsWkbPtr(
new unsigned char[size], size );
1554 QList< QList<QgsWkbPtr> >::const_iterator outerWkbIt = mCurrentWKBFragments.constBegin();
1556 for ( ; outerWkbIt != mCurrentWKBFragments.constEnd(); ++outerWkbIt )
1561 QList<QgsWkbPtr>::const_iterator innerWkbIt = outerWkbIt->constBegin();
1562 for ( ; innerWkbIt != outerWkbIt->constEnd(); ++innerWkbIt )
1564 memcpy( wkbPtr, *innerWkbIt, innerWkbIt->size() );
1565 wkbPtr += innerWkbIt->
size();
1566 delete[] *innerWkbIt;
1570 mCurrentWKBFragments.clear();
1575 int QgsGmlStreamingParser::totalWKBFragmentSize()
const
1578 const auto constMCurrentWKBFragments = mCurrentWKBFragments;
1579 for (
const QList<QgsWkbPtr> &list : constMCurrentWKBFragments )
1581 const auto constList = list;
1590 void QgsGmlStreamingParser::createParser(
const QByteArray &encoding )
1592 Q_ASSERT( !mParser );
1594 mParser = XML_ParserCreateNS( encoding.isEmpty() ?
nullptr : encoding.data(),
NS_SEPARATOR );
1595 XML_SetUserData( mParser,
this );
1596 XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
1597 XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
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 axis is inverted (e.g., for WMS 1.3) for the CRS.
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.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
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.
QgsWkbTypes::Type wkbType() const
Returns the geometry type.
QPair< QgsFeature *, QString > QgsGmlFeaturePtrGmlIdPair
int numberReturned() const
Returns WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found.
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.
int getFeatures(const QString &uri, QgsWkbTypes::Type *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 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.
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.
A class to represent a 2D point.
A rectangle specified with double values.
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
void set(const QgsPointXY &p1, const QgsPointXY &p2, bool normalize=true)
Sets the rectangle from two QgsPoints.
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
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 is empty.
Handles storage of information regarding WKB types and their properties.
Type
The WKB type describes the number of dimensions a geometry has.
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 QgsSetRequestInitiatorClass(request, _class)
const QgsCoordinateReferenceSystem & crs