28#include <QNetworkReply>
29#include <QNetworkRequest>
30#include <QProgressDialog>
36#include "moc_qgsgmlschema.cpp"
38using namespace Qt::StringLiterals;
40#ifndef NS_SEPARATOR_DEFINED
41#define NS_SEPARATOR_DEFINED
42static const char NS_SEPARATOR =
'?';
45#define GML_NAMESPACE u"http://www.opengis.net/gml"_s
56 for (
int i = 0; i < mFields.size(); i++ )
58 if ( mFields[i].name() == name )
return i;
65 : mSkipLevel( std::numeric_limits<int>::max() )
67 mGeometryTypes << u
"Point"_s << u
"MultiPoint"_s
68 << u
"LineString"_s << u
"MultiLineString"_s
69 << u
"Polygon"_s << u
"MultiPolygon"_s;
72QString QgsGmlSchema::readAttribute(
const QString &attributeName,
const XML_Char **attr )
const
77 if ( attributeName.compare( attr[i] ) == 0 )
79 return QString( attr[i + 1] );
92 if ( !dom.setContent( xml,
false, &errorMsg, &errorLine, &errorColumn ) )
98 const QDomElement docElem = dom.documentElement();
100 const QList<QDomElement> elementElements = domElements( docElem, u
"element"_s );
104 const auto constElementElements = elementElements;
105 for (
const QDomElement &elementElement : constElementElements )
107 const QString name = elementElement.attribute( u
"name"_s );
108 const QString type = elementElement.attribute( u
"type"_s );
110 const QString gmlBaseType = xsdComplexTypeGmlBaseType( docElem, stripNS( type ) );
117 if ( gmlBaseType ==
"AbstractFeatureType"_L1 )
121 xsdFeatureClass( docElem, stripNS( type ), featureClass );
122 mFeatureClassMap.insert( name, featureClass );
130bool QgsGmlSchema::xsdFeatureClass(
const QDomElement &element,
const QString &typeName,
QgsGmlFeatureClass &featureClass )
133 const QDomElement complexTypeElement = domElement( element, u
"complexType"_s, u
"name"_s, typeName );
134 if ( complexTypeElement.isNull() )
return false;
137 QDomElement extrest = domElement( complexTypeElement, u
"complexContent.extension"_s );
138 if ( extrest.isNull() )
140 extrest = domElement( complexTypeElement, u
"complexContent.restriction"_s );
142 if ( extrest.isNull() )
return false;
144 const QString extrestName = extrest.attribute( u
"base"_s );
145 if ( extrestName ==
"gml:AbstractFeatureType"_L1 )
154 if ( !xsdFeatureClass( element, stripNS( extrestName ), featureClass ) )
return false;
158 QStringList geometryPropertyTypes;
159 const auto constMGeometryTypes = mGeometryTypes;
160 for (
const QString &geom : constMGeometryTypes )
162 geometryPropertyTypes << geom +
"PropertyType";
165 QStringList geometryAliases;
166 geometryAliases << u
"location"_s << u
"centerOf"_s << u
"position"_s << u
"extentOf"_s
167 << u
"coverage"_s << u
"edgeOf"_s << u
"centerLineOf"_s << u
"multiLocation"_s
168 << u
"multiCenterOf"_s << u
"multiPosition"_s << u
"multiCenterLineOf"_s
169 << u
"multiEdgeOf"_s << u
"multiCoverage"_s << u
"multiExtentOf"_s;
172 const QList<QDomElement> sequenceElements = domElements( extrest, u
"sequence.element"_s );
173 const auto constSequenceElements = sequenceElements;
174 for (
const QDomElement &sequenceElement : constSequenceElements )
176 const QString fieldName = sequenceElement.attribute( u
"name"_s );
177 QString fieldTypeName = stripNS( sequenceElement.attribute( u
"type"_s ) );
178 const QString ref = sequenceElement.attribute( u
"ref"_s );
181 if ( !ref.isEmpty() )
183 if ( ref.startsWith(
"gml:"_L1 ) )
185 if ( geometryAliases.contains( stripNS( ref ) ) )
191 QgsDebugError( u
"Unknown referenced GML element: %1"_s.arg( ref ) );
197 QgsDebugError( u
"field %1.%2 is referencing %3 - not supported"_s.arg( typeName, fieldName ) );
202 if ( fieldName.isEmpty() )
204 QgsDebugError( u
"field in %1 without name"_s.arg( typeName ) );
209 if ( fieldTypeName.isEmpty() )
212 const QDomElement sequenceElementRestriction = domElement( sequenceElement, u
"simpleType.restriction"_s );
213 fieldTypeName = stripNS( sequenceElementRestriction.attribute( u
"base"_s ) );
216 QMetaType::Type fieldType = QMetaType::Type::QString;
217 if ( fieldTypeName.isEmpty() )
219 QgsDebugError( u
"Cannot get %1.%2 field type"_s.arg( typeName, fieldName ) );
223 if ( geometryPropertyTypes.contains( fieldTypeName ) )
230 if ( fieldTypeName ==
"decimal"_L1 )
232 fieldType = QMetaType::Type::Double;
234 else if ( fieldTypeName ==
"integer"_L1 )
236 fieldType = QMetaType::Type::Int;
240 const QgsField field( fieldName, fieldType, fieldTypeName );
241 featureClass.
fields().append( field );
247QString QgsGmlSchema::xsdComplexTypeGmlBaseType(
const QDomElement &element,
const QString &name )
250 const QDomElement complexTypeElement = domElement( element, u
"complexType"_s, u
"name"_s, name );
251 if ( complexTypeElement.isNull() )
return QString();
253 QDomElement extrest = domElement( complexTypeElement, u
"complexContent.extension"_s );
254 if ( extrest.isNull() )
256 extrest = domElement( complexTypeElement, u
"complexContent.restriction"_s );
258 if ( extrest.isNull() )
return QString();
260 const QString extrestName = extrest.attribute( u
"base"_s );
261 if ( extrestName.startsWith(
"gml:"_L1 ) )
264 return stripNS( extrestName );
267 return xsdComplexTypeGmlBaseType( element, stripNS( extrestName ) );
270QString QgsGmlSchema::stripNS(
const QString &name )
272 return name.contains(
':' ) ? name.section(
':', 1 ) : name;
275QList<QDomElement> QgsGmlSchema::domElements(
const QDomElement &element,
const QString &path )
277 QList<QDomElement> list;
279 QStringList names = path.split(
'.' );
280 if ( names.isEmpty() )
return list;
281 const QString name = names.value( 0 );
284 QDomNode n1 = element.firstChild();
285 while ( !n1.isNull() )
287 const QDomElement el = n1.toElement();
290 const QString tagName = stripNS( el.tagName() );
291 if ( tagName == name )
293 if ( names.isEmpty() )
299 list.append( domElements( el, names.join(
'.'_L1 ) ) );
303 n1 = n1.nextSibling();
309QDomElement QgsGmlSchema::domElement(
const QDomElement &element,
const QString &path )
311 return domElements( element, path ).value( 0 );
314QList<QDomElement> QgsGmlSchema::domElements( QList<QDomElement> &elements,
const QString &attr,
const QString &attrVal )
316 QList<QDomElement> list;
317 const auto constElements = elements;
318 for (
const QDomElement &el : constElements )
320 if ( el.attribute( attr ) == attrVal )
328QDomElement QgsGmlSchema::domElement(
const QDomElement &element,
const QString &path,
const QString &attr,
const QString &attrVal )
330 QList<QDomElement> list = domElements( element, path );
331 return domElements( list, attr, attrVal ).value( 0 );
337 mSkipLevel = std::numeric_limits<int>::max();
338 XML_Parser p = XML_ParserCreateNS(
nullptr, NS_SEPARATOR );
339 XML_SetUserData( p,
this );
340 XML_SetElementHandler( p, QgsGmlSchema::start, QgsGmlSchema::end );
341 XML_SetCharacterDataHandler( p, QgsGmlSchema::chars );
343 const int res = XML_Parse( p, data.constData(), data.size(), atEnd );
347 const QString err = QString( XML_ErrorString( XML_GetErrorCode( p ) ) );
348 QgsDebugError( u
"XML_Parse returned %1 error %2"_s.arg( res ).arg( err ) );
349 mError =
QgsError( err, u
"GML schema"_s );
350 mError.append( tr(
"Cannot guess schema" ) );
356void QgsGmlSchema::startElement(
const XML_Char *el,
const XML_Char **attr )
361 const QString elementName = QString::fromUtf8( el );
362 QgsDebugMsgLevel( u
"-> %1 %2 %3"_s.arg( mLevel ).arg( elementName, mLevel >= mSkipLevel ?
"skip" :
"" ), 5 );
364 if ( mLevel >= mSkipLevel )
370 mParsePathStack.append( elementName );
371 const QString path = mParsePathStack.join(
'.'_L1 );
373 QStringList splitName = elementName.split( NS_SEPARATOR );
374 const QString localName = splitName.last();
375 const QString ns = splitName.size() > 1 ? splitName.first() : QString();
378 const ParseMode parseMode = modeStackTop();
384 mSkipLevel = mLevel + 1;
386 else if ( localName.compare(
"featureMembers"_L1, Qt::CaseInsensitive ) == 0 )
388 mParseModeStack.push( QgsGmlSchema::FeatureMembers );
395 else if ( localName.endsWith(
"member"_L1, Qt::CaseInsensitive ) )
397 mParseModeStack.push( QgsGmlSchema::FeatureMember );
400 else if ( elementName.endsWith(
"_layer"_L1 ) )
408 else if ( elementName.endsWith(
"_feature"_L1 )
409 || parseMode == QgsGmlSchema::FeatureMember
410 || parseMode == QgsGmlSchema::FeatureMembers
411 || localName.compare(
"feature"_L1, Qt::CaseInsensitive ) == 0 )
414 if ( mFeatureClassMap.count( localName ) == 0 )
416 mFeatureClassMap.insert( localName, QgsGmlFeatureClass( localName, path ) );
418 mCurrentFeatureName = localName;
419 mParseModeStack.push( QgsGmlSchema::Feature );
421 else if ( parseMode == QgsGmlSchema::Attribute && ns ==
GML_NAMESPACE && mGeometryTypes.indexOf( localName ) >= 0 )
424 QStringList &
geometryAttributes = mFeatureClassMap[mCurrentFeatureName].geometryAttributes();
429 mSkipLevel = mLevel + 1;
431 else if ( parseMode == QgsGmlSchema::Feature )
440 const QString name = readAttribute( u
"name"_s, attr );
442 if ( localName.compare(
"attribute"_L1, Qt::CaseInsensitive ) == 0
445 const QString value = readAttribute( u
"value"_s, attr );
447 addAttribute( name, value );
451 mAttributeName = localName;
452 mParseModeStack.push( QgsGmlSchema::Attribute );
458void QgsGmlSchema::endElement(
const XML_Char *el )
460 const QString elementName = QString::fromUtf8( el );
463 if ( mLevel >= mSkipLevel )
472 mSkipLevel = std::numeric_limits<int>::max();
475 QStringList splitName = elementName.split( NS_SEPARATOR );
476 const QString localName = splitName.last();
477 const QString ns = splitName.size() > 1 ? splitName.first() : QString();
479 const QgsGmlSchema::ParseMode parseMode = modeStackTop();
481 if ( parseMode == QgsGmlSchema::FeatureMembers )
485 else if ( parseMode == QgsGmlSchema::Attribute && localName == mAttributeName )
491 if ( mFeatureClassMap[mCurrentFeatureName].
geometryAttributes().count( mAttributeName ) == 0 )
493 addAttribute( mAttributeName, mStringCash );
496 else if ( ns ==
GML_NAMESPACE && localName ==
"boundedBy"_L1 )
500 else if ( localName.endsWith(
"member"_L1, Qt::CaseInsensitive ) )
504 mParsePathStack.removeLast();
508void QgsGmlSchema::characters(
const XML_Char *chars,
int len )
511 if ( mLevel >= mSkipLevel )
518 if ( modeStackTop() == QgsGmlSchema::Attribute )
520 mStringCash.append( QString::fromUtf8( chars, len ) );
524void QgsGmlSchema::addAttribute(
const QString &name,
const QString &value )
528 ( void ) value.toInt( &ok );
529 QMetaType::Type type = QMetaType::Type::QString;
532 type = QMetaType::Type::Int;
536 ( void ) value.toDouble( &ok );
539 type = QMetaType::Type::Double;
544 QList<QgsField> &
fields = mFeatureClassMap[mCurrentFeatureName].fields();
545 const int fieldIndex = mFeatureClassMap[mCurrentFeatureName].fieldIndex( name );
546 if ( fieldIndex == -1 )
548 const QgsField field( name, type );
553 QgsField &field =
fields[fieldIndex];
555 if ( ( field.
type() == QMetaType::Type::Int && ( type == QMetaType::Type::QString || type == QMetaType::Type::Double ) ) ||
556 ( field.
type() == QMetaType::Type::Double && type == QMetaType::Type::QString ) )
565 return mFeatureClassMap.keys();
570 if ( mFeatureClassMap.count( typeName ) == 0 )
return QList<QgsField>();
571 return mFeatureClassMap[typeName].fields();
576 if ( mFeatureClassMap.count( typeName ) == 0 )
return QStringList();
577 return mFeatureClassMap[typeName].geometryAttributes();
A container for error messages.
void setType(QMetaType::Type type)
Set variant type.
Description of feature class in GML.
int fieldIndex(const QString &name)
QStringList & geometryAttributes()
QList< QgsField > & fields()
QgsGmlFeatureClass()=default
bool parseXSD(const QByteArray &xml)
Gets fields info from XSD.
QList< QgsField > fields(const QString &typeName)
Gets fields for type/class name parsed from GML or XSD.
QStringList geometryAttributes(const QString &typeName)
Gets list of geometry attributes for type/class name.
bool guessSchema(const QByteArray &data)
Guess GML schema from data if XSD does not exist.
QStringList typeNames() const
Gets list of dot separated paths to feature classes parsed from GML or XSD.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)