28#include <QNetworkReply>
29#include <QNetworkRequest>
30#include <QProgressDialog>
35#include "moc_qgsgmlschema.cpp"
37#ifndef NS_SEPARATOR_DEFINED
38#define NS_SEPARATOR_DEFINED
39static const char NS_SEPARATOR =
'?';
42#define GML_NAMESPACE QStringLiteral( "http://www.opengis.net/gml" )
53 for (
int i = 0; i < mFields.size(); i++ )
55 if ( mFields[i].name() == name )
return i;
62 : mSkipLevel( std::numeric_limits<int>::max() )
64 mGeometryTypes << QStringLiteral(
"Point" ) << QStringLiteral(
"MultiPoint" )
65 << QStringLiteral(
"LineString" ) << QStringLiteral(
"MultiLineString" )
66 << QStringLiteral(
"Polygon" ) << QStringLiteral(
"MultiPolygon" );
69QString QgsGmlSchema::readAttribute(
const QString &attributeName,
const XML_Char **attr )
const
74 if ( attributeName.compare( attr[i] ) == 0 )
76 return QString( attr[i + 1] );
89 if ( !dom.setContent( xml,
false, &errorMsg, &errorLine, &errorColumn ) )
95 const QDomElement docElem = dom.documentElement();
97 const QList<QDomElement> elementElements = domElements( docElem, QStringLiteral(
"element" ) );
101 const auto constElementElements = elementElements;
102 for (
const QDomElement &elementElement : constElementElements )
104 const QString name = elementElement.attribute( QStringLiteral(
"name" ) );
105 const QString type = elementElement.attribute( QStringLiteral(
"type" ) );
107 const QString gmlBaseType = xsdComplexTypeGmlBaseType( docElem, stripNS( type ) );
114 if ( gmlBaseType == QLatin1String(
"AbstractFeatureType" ) )
118 xsdFeatureClass( docElem, stripNS( type ), featureClass );
119 mFeatureClassMap.insert( name, featureClass );
127bool QgsGmlSchema::xsdFeatureClass(
const QDomElement &element,
const QString &typeName,
QgsGmlFeatureClass &featureClass )
130 const QDomElement complexTypeElement = domElement( element, QStringLiteral(
"complexType" ), QStringLiteral(
"name" ), typeName );
131 if ( complexTypeElement.isNull() )
return false;
134 QDomElement extrest = domElement( complexTypeElement, QStringLiteral(
"complexContent.extension" ) );
135 if ( extrest.isNull() )
137 extrest = domElement( complexTypeElement, QStringLiteral(
"complexContent.restriction" ) );
139 if ( extrest.isNull() )
return false;
141 const QString extrestName = extrest.attribute( QStringLiteral(
"base" ) );
142 if ( extrestName == QLatin1String(
"gml:AbstractFeatureType" ) )
151 if ( !xsdFeatureClass( element, stripNS( extrestName ), featureClass ) )
return false;
155 QStringList geometryPropertyTypes;
156 const auto constMGeometryTypes = mGeometryTypes;
157 for (
const QString &geom : constMGeometryTypes )
159 geometryPropertyTypes << geom +
"PropertyType";
162 QStringList geometryAliases;
163 geometryAliases << QStringLiteral(
"location" ) << QStringLiteral(
"centerOf" ) << QStringLiteral(
"position" ) << QStringLiteral(
"extentOf" )
164 << QStringLiteral(
"coverage" ) << QStringLiteral(
"edgeOf" ) << QStringLiteral(
"centerLineOf" ) << QStringLiteral(
"multiLocation" )
165 << QStringLiteral(
"multiCenterOf" ) << QStringLiteral(
"multiPosition" ) << QStringLiteral(
"multiCenterLineOf" )
166 << QStringLiteral(
"multiEdgeOf" ) << QStringLiteral(
"multiCoverage" ) << QStringLiteral(
"multiExtentOf" );
169 const QList<QDomElement> sequenceElements = domElements( extrest, QStringLiteral(
"sequence.element" ) );
170 const auto constSequenceElements = sequenceElements;
171 for (
const QDomElement &sequenceElement : constSequenceElements )
173 const QString fieldName = sequenceElement.attribute( QStringLiteral(
"name" ) );
174 QString fieldTypeName = stripNS( sequenceElement.attribute( QStringLiteral(
"type" ) ) );
175 const QString ref = sequenceElement.attribute( QStringLiteral(
"ref" ) );
178 if ( !ref.isEmpty() )
180 if ( ref.startsWith( QLatin1String(
"gml:" ) ) )
182 if ( geometryAliases.contains( stripNS( ref ) ) )
188 QgsDebugError( QStringLiteral(
"Unknown referenced GML element: %1" ).arg( ref ) );
194 QgsDebugError( QStringLiteral(
"field %1.%2 is referencing %3 - not supported" ).arg( typeName, fieldName ) );
199 if ( fieldName.isEmpty() )
201 QgsDebugError( QStringLiteral(
"field in %1 without name" ).arg( typeName ) );
206 if ( fieldTypeName.isEmpty() )
209 const QDomElement sequenceElementRestriction = domElement( sequenceElement, QStringLiteral(
"simpleType.restriction" ) );
210 fieldTypeName = stripNS( sequenceElementRestriction.attribute( QStringLiteral(
"base" ) ) );
213 QMetaType::Type fieldType = QMetaType::Type::QString;
214 if ( fieldTypeName.isEmpty() )
216 QgsDebugError( QStringLiteral(
"Cannot get %1.%2 field type" ).arg( typeName, fieldName ) );
220 if ( geometryPropertyTypes.contains( fieldTypeName ) )
227 if ( fieldTypeName == QLatin1String(
"decimal" ) )
229 fieldType = QMetaType::Type::Double;
231 else if ( fieldTypeName == QLatin1String(
"integer" ) )
233 fieldType = QMetaType::Type::Int;
237 const QgsField field( fieldName, fieldType, fieldTypeName );
238 featureClass.
fields().append( field );
244QString QgsGmlSchema::xsdComplexTypeGmlBaseType(
const QDomElement &element,
const QString &name )
247 const QDomElement complexTypeElement = domElement( element, QStringLiteral(
"complexType" ), QStringLiteral(
"name" ), name );
248 if ( complexTypeElement.isNull() )
return QString();
250 QDomElement extrest = domElement( complexTypeElement, QStringLiteral(
"complexContent.extension" ) );
251 if ( extrest.isNull() )
253 extrest = domElement( complexTypeElement, QStringLiteral(
"complexContent.restriction" ) );
255 if ( extrest.isNull() )
return QString();
257 const QString extrestName = extrest.attribute( QStringLiteral(
"base" ) );
258 if ( extrestName.startsWith( QLatin1String(
"gml:" ) ) )
261 return stripNS( extrestName );
264 return xsdComplexTypeGmlBaseType( element, stripNS( extrestName ) );
267QString QgsGmlSchema::stripNS(
const QString &name )
269 return name.contains(
':' ) ? name.section(
':', 1 ) : name;
272QList<QDomElement> QgsGmlSchema::domElements(
const QDomElement &element,
const QString &path )
274 QList<QDomElement> list;
276 QStringList names = path.split(
'.' );
277 if ( names.isEmpty() )
return list;
278 const QString name = names.value( 0 );
281 QDomNode n1 = element.firstChild();
282 while ( !n1.isNull() )
284 const QDomElement el = n1.toElement();
287 const QString tagName = stripNS( el.tagName() );
288 if ( tagName == name )
290 if ( names.isEmpty() )
296 list.append( domElements( el, names.join( QLatin1Char(
'.' ) ) ) );
300 n1 = n1.nextSibling();
306QDomElement QgsGmlSchema::domElement(
const QDomElement &element,
const QString &path )
308 return domElements( element, path ).value( 0 );
311QList<QDomElement> QgsGmlSchema::domElements( QList<QDomElement> &elements,
const QString &attr,
const QString &attrVal )
313 QList<QDomElement> list;
314 const auto constElements = elements;
315 for (
const QDomElement &el : constElements )
317 if ( el.attribute( attr ) == attrVal )
325QDomElement QgsGmlSchema::domElement(
const QDomElement &element,
const QString &path,
const QString &attr,
const QString &attrVal )
327 QList<QDomElement> list = domElements( element, path );
328 return domElements( list, attr, attrVal ).value( 0 );
334 mSkipLevel = std::numeric_limits<int>::max();
335 XML_Parser p = XML_ParserCreateNS(
nullptr, NS_SEPARATOR );
336 XML_SetUserData( p,
this );
337 XML_SetElementHandler( p, QgsGmlSchema::start, QgsGmlSchema::end );
338 XML_SetCharacterDataHandler( p, QgsGmlSchema::chars );
340 const int res = XML_Parse( p, data.constData(), data.size(), atEnd );
344 const QString err = QString( XML_ErrorString( XML_GetErrorCode( p ) ) );
345 QgsDebugError( QStringLiteral(
"XML_Parse returned %1 error %2" ).arg( res ).arg( err ) );
346 mError =
QgsError( err, QStringLiteral(
"GML schema" ) );
347 mError.append( tr(
"Cannot guess schema" ) );
353void QgsGmlSchema::startElement(
const XML_Char *el,
const XML_Char **attr )
358 const QString elementName = QString::fromUtf8( el );
359 QgsDebugMsgLevel( QStringLiteral(
"-> %1 %2 %3" ).arg( mLevel ).arg( elementName, mLevel >= mSkipLevel ?
"skip" :
"" ), 5 );
361 if ( mLevel >= mSkipLevel )
367 mParsePathStack.append( elementName );
368 const QString path = mParsePathStack.join( QLatin1Char(
'.' ) );
370 QStringList splitName = elementName.split( NS_SEPARATOR );
371 const QString localName = splitName.last();
372 const QString ns = splitName.size() > 1 ? splitName.first() : QString();
375 const ParseMode parseMode = modeStackTop();
378 if ( ns ==
GML_NAMESPACE && localName == QLatin1String(
"boundedBy" ) )
381 mSkipLevel = mLevel + 1;
383 else if ( localName.compare( QLatin1String(
"featureMembers" ), Qt::CaseInsensitive ) == 0 )
385 mParseModeStack.push( QgsGmlSchema::FeatureMembers );
392 else if ( localName.endsWith( QLatin1String(
"member" ), Qt::CaseInsensitive ) )
394 mParseModeStack.push( QgsGmlSchema::FeatureMember );
397 else if ( elementName.endsWith( QLatin1String(
"_layer" ) ) )
405 else if ( elementName.endsWith( QLatin1String(
"_feature" ) )
406 || parseMode == QgsGmlSchema::FeatureMember
407 || parseMode == QgsGmlSchema::FeatureMembers
408 || localName.compare( QLatin1String(
"feature" ), Qt::CaseInsensitive ) == 0 )
411 if ( mFeatureClassMap.count( localName ) == 0 )
413 mFeatureClassMap.insert( localName, QgsGmlFeatureClass( localName, path ) );
415 mCurrentFeatureName = localName;
416 mParseModeStack.push( QgsGmlSchema::Feature );
418 else if ( parseMode == QgsGmlSchema::Attribute && ns ==
GML_NAMESPACE && mGeometryTypes.indexOf( localName ) >= 0 )
421 QStringList &
geometryAttributes = mFeatureClassMap[mCurrentFeatureName].geometryAttributes();
426 mSkipLevel = mLevel + 1;
428 else if ( parseMode == QgsGmlSchema::Feature )
437 const QString name = readAttribute( QStringLiteral(
"name" ), attr );
439 if ( localName.compare( QLatin1String(
"attribute" ), Qt::CaseInsensitive ) == 0
442 const QString value = readAttribute( QStringLiteral(
"value" ), attr );
444 addAttribute( name, value );
448 mAttributeName = localName;
449 mParseModeStack.push( QgsGmlSchema::Attribute );
455void QgsGmlSchema::endElement(
const XML_Char *el )
457 const QString elementName = QString::fromUtf8( el );
458 QgsDebugMsgLevel( QStringLiteral(
"<- %1 %2" ).arg( mLevel ).arg( elementName ), 5 );
460 if ( mLevel >= mSkipLevel )
469 mSkipLevel = std::numeric_limits<int>::max();
472 QStringList splitName = elementName.split( NS_SEPARATOR );
473 const QString localName = splitName.last();
474 const QString ns = splitName.size() > 1 ? splitName.first() : QString();
476 const QgsGmlSchema::ParseMode parseMode = modeStackTop();
478 if ( parseMode == QgsGmlSchema::FeatureMembers )
482 else if ( parseMode == QgsGmlSchema::Attribute && localName == mAttributeName )
488 if ( mFeatureClassMap[mCurrentFeatureName].
geometryAttributes().count( mAttributeName ) == 0 )
490 addAttribute( mAttributeName, mStringCash );
493 else if ( ns ==
GML_NAMESPACE && localName == QLatin1String(
"boundedBy" ) )
497 else if ( localName.endsWith( QLatin1String(
"member" ), Qt::CaseInsensitive ) )
501 mParsePathStack.removeLast();
505void QgsGmlSchema::characters(
const XML_Char *chars,
int len )
508 if ( mLevel >= mSkipLevel )
515 if ( modeStackTop() == QgsGmlSchema::Attribute )
517 mStringCash.append( QString::fromUtf8( chars, len ) );
521void QgsGmlSchema::addAttribute(
const QString &name,
const QString &value )
525 ( void ) value.toInt( &ok );
526 QMetaType::Type type = QMetaType::Type::QString;
529 type = QMetaType::Type::Int;
533 ( void ) value.toDouble( &ok );
536 type = QMetaType::Type::Double;
541 QList<QgsField> &
fields = mFeatureClassMap[mCurrentFeatureName].fields();
542 const int fieldIndex = mFeatureClassMap[mCurrentFeatureName].fieldIndex( name );
543 if ( fieldIndex == -1 )
545 const QgsField field( name, type );
550 QgsField &field =
fields[fieldIndex];
552 if ( ( field.
type() == QMetaType::Type::Int && ( type == QMetaType::Type::QString || type == QMetaType::Type::Double ) ) ||
553 ( field.
type() == QMetaType::Type::Double && type == QMetaType::Type::QString ) )
562 return mFeatureClassMap.keys();
567 if ( mFeatureClassMap.count( typeName ) == 0 )
return QList<QgsField>();
568 return mFeatureClassMap[typeName].fields();
573 if ( mFeatureClassMap.count( typeName ) == 0 )
return QStringList();
574 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)