16#include "moc_qgsgmlschema.cpp" 
   25#include <QNetworkRequest> 
   26#include <QNetworkReply> 
   27#include <QProgressDialog> 
   34#ifndef NS_SEPARATOR_DEFINED 
   35#define NS_SEPARATOR_DEFINED 
   36static const char NS_SEPARATOR = 
'?';
 
   39#define GML_NAMESPACE QStringLiteral( "http://www.opengis.net/gml" ) 
   50  for ( 
int i = 0; i < mFields.size(); i++ )
 
   52    if ( mFields[i].name() == name ) 
return i;
 
 
   59  : mSkipLevel( std::numeric_limits<int>::max() )
 
   61  mGeometryTypes << QStringLiteral( 
"Point" ) << QStringLiteral( 
"MultiPoint" )
 
   62                 << QStringLiteral( 
"LineString" ) << QStringLiteral( 
"MultiLineString" )
 
   63                 << QStringLiteral( 
"Polygon" ) << QStringLiteral( 
"MultiPolygon" );
 
 
   66QString QgsGmlSchema::readAttribute( 
const QString &attributeName, 
const XML_Char **attr )
 const 
   71    if ( attributeName.compare( attr[i] ) == 0 )
 
   73      return QString( attr[i + 1] );
 
   86  if ( !dom.setContent( xml, 
false, &errorMsg, &errorLine, &errorColumn ) )
 
   92  const QDomElement docElem = dom.documentElement();
 
   94  const QList<QDomElement> elementElements = domElements( docElem, QStringLiteral( 
"element" ) );
 
   98  const auto constElementElements = elementElements;
 
   99  for ( 
const QDomElement &elementElement : constElementElements )
 
  101    const QString name = elementElement.attribute( QStringLiteral( 
"name" ) );
 
  102    const QString type = elementElement.attribute( QStringLiteral( 
"type" ) );
 
  104    const QString gmlBaseType = xsdComplexTypeGmlBaseType( docElem, stripNS( type ) );
 
  111    if ( gmlBaseType == QLatin1String( 
"AbstractFeatureType" ) )
 
  115      xsdFeatureClass( docElem, stripNS( type ), featureClass );
 
  116      mFeatureClassMap.insert( name, featureClass );
 
 
  127  const QDomElement complexTypeElement = domElement( element, QStringLiteral( 
"complexType" ), QStringLiteral( 
"name" ), 
typeName );
 
  128  if ( complexTypeElement.isNull() ) 
return false;
 
  131  QDomElement extrest = domElement( complexTypeElement, QStringLiteral( 
"complexContent.extension" ) );
 
  132  if ( extrest.isNull() )
 
  134    extrest = domElement( complexTypeElement, QStringLiteral( 
"complexContent.restriction" ) );
 
  136  if ( extrest.isNull() ) 
return false;
 
  138  const QString extrestName = extrest.attribute( QStringLiteral( 
"base" ) );
 
  139  if ( extrestName == QLatin1String( 
"gml:AbstractFeatureType" ) )
 
  148    if ( !xsdFeatureClass( element, stripNS( extrestName ), featureClass ) ) 
return false;
 
  152  QStringList geometryPropertyTypes;
 
  153  const auto constMGeometryTypes = mGeometryTypes;
 
  154  for ( 
const QString &geom : constMGeometryTypes )
 
  156    geometryPropertyTypes << geom + 
"PropertyType";
 
  159  QStringList geometryAliases;
 
  160  geometryAliases << QStringLiteral( 
"location" ) << QStringLiteral( 
"centerOf" ) << QStringLiteral( 
"position" ) << QStringLiteral( 
"extentOf" )
 
  161                  << QStringLiteral( 
"coverage" ) << QStringLiteral( 
"edgeOf" ) << QStringLiteral( 
"centerLineOf" ) << QStringLiteral( 
"multiLocation" )
 
  162                  << QStringLiteral( 
"multiCenterOf" ) << QStringLiteral( 
"multiPosition" ) << QStringLiteral( 
"multiCenterLineOf" )
 
  163                  << QStringLiteral( 
"multiEdgeOf" ) << QStringLiteral( 
"multiCoverage" ) << QStringLiteral( 
"multiExtentOf" );
 
  166  const QList<QDomElement> sequenceElements = domElements( extrest, QStringLiteral( 
"sequence.element" ) );
 
  167  const auto constSequenceElements = sequenceElements;
 
  168  for ( 
const QDomElement &sequenceElement : constSequenceElements )
 
  170    const QString fieldName = sequenceElement.attribute( QStringLiteral( 
"name" ) );
 
  171    QString fieldTypeName = stripNS( sequenceElement.attribute( QStringLiteral( 
"type" ) ) );
 
  172    const QString ref = sequenceElement.attribute( QStringLiteral( 
"ref" ) );
 
  175    if ( !ref.isEmpty() )
 
  177      if ( ref.startsWith( QLatin1String( 
"gml:" ) ) )
 
  179        if ( geometryAliases.contains( stripNS( ref ) ) )
 
  185          QgsDebugError( QStringLiteral( 
"Unknown referenced GML element: %1" ).arg( ref ) );
 
  191        QgsDebugError( QStringLiteral( 
"field %1.%2 is referencing %3 - not supported" ).arg( 
typeName, fieldName ) );
 
  196    if ( fieldName.isEmpty() )
 
  203    if ( fieldTypeName.isEmpty() )
 
  206      const QDomElement sequenceElementRestriction = domElement( sequenceElement, QStringLiteral( 
"simpleType.restriction" ) );
 
  207      fieldTypeName = stripNS( sequenceElementRestriction.attribute( QStringLiteral( 
"base" ) ) );
 
  210    QMetaType::Type fieldType = QMetaType::Type::QString;
 
  211    if ( fieldTypeName.isEmpty() )
 
  217      if ( geometryPropertyTypes.contains( fieldTypeName ) )
 
  224      if ( fieldTypeName == QLatin1String( 
"decimal" ) )
 
  226        fieldType = QMetaType::Type::Double;
 
  228      else if ( fieldTypeName == QLatin1String( 
"integer" ) )
 
  230        fieldType = QMetaType::Type::Int;
 
  234    const QgsField field( fieldName, fieldType, fieldTypeName );
 
  235    featureClass.
fields().append( field );
 
  241QString QgsGmlSchema::xsdComplexTypeGmlBaseType( 
const QDomElement &element, 
const QString &name )
 
  244  const QDomElement complexTypeElement = domElement( element, QStringLiteral( 
"complexType" ), QStringLiteral( 
"name" ), name );
 
  245  if ( complexTypeElement.isNull() ) 
return QString();
 
  247  QDomElement extrest = domElement( complexTypeElement, QStringLiteral( 
"complexContent.extension" ) );
 
  248  if ( extrest.isNull() )
 
  250    extrest = domElement( complexTypeElement, QStringLiteral( 
"complexContent.restriction" ) );
 
  252  if ( extrest.isNull() ) 
return QString();
 
  254  const QString extrestName = extrest.attribute( QStringLiteral( 
"base" ) );
 
  255  if ( extrestName.startsWith( QLatin1String( 
"gml:" ) ) )
 
  258    return stripNS( extrestName );
 
  261  return xsdComplexTypeGmlBaseType( element, stripNS( extrestName ) );
 
  264QString QgsGmlSchema::stripNS( 
const QString &name )
 
  266  return name.contains( 
':' ) ? name.section( 
':', 1 ) : name;
 
  269QList<QDomElement> QgsGmlSchema::domElements( 
const QDomElement &element, 
const QString &path )
 
  271  QList<QDomElement> list;
 
  273  QStringList names = path.split( 
'.' );
 
  274  if ( names.isEmpty() ) 
return list;
 
  275  const QString name = names.value( 0 );
 
  278  QDomNode n1 = element.firstChild();
 
  279  while ( !n1.isNull() )
 
  281    const QDomElement el = n1.toElement();
 
  284      const QString tagName = stripNS( el.tagName() );
 
  285      if ( tagName == name )
 
  287        if ( names.isEmpty() )
 
  293          list.append( domElements( el, names.join( QLatin1Char( 
'.' ) ) ) );
 
  297    n1 = n1.nextSibling();
 
  303QDomElement QgsGmlSchema::domElement( 
const QDomElement &element, 
const QString &path )
 
  305  return domElements( element, path ).value( 0 );
 
  308QList<QDomElement> QgsGmlSchema::domElements( QList<QDomElement> &elements, 
const QString &attr, 
const QString &attrVal )
 
  310  QList<QDomElement> list;
 
  311  const auto constElements = elements;
 
  312  for ( 
const QDomElement &el : constElements )
 
  314    if ( el.attribute( attr ) == attrVal )
 
  322QDomElement QgsGmlSchema::domElement( 
const QDomElement &element, 
const QString &path, 
const QString &attr, 
const QString &attrVal )
 
  324  QList<QDomElement> list = domElements( element, path );
 
  325  return domElements( list, attr, attrVal ).value( 0 );
 
  331  mSkipLevel = std::numeric_limits<int>::max();
 
  332  XML_Parser p = XML_ParserCreateNS( 
nullptr, NS_SEPARATOR );
 
  333  XML_SetUserData( p, 
this );
 
  334  XML_SetElementHandler( p, QgsGmlSchema::start, QgsGmlSchema::end );
 
  335  XML_SetCharacterDataHandler( p, QgsGmlSchema::chars );
 
  337  const int res = XML_Parse( p, data.constData(), data.size(), atEnd );
 
  341    const QString err = QString( XML_ErrorString( XML_GetErrorCode( p ) ) );
 
  342    QgsDebugError( QStringLiteral( 
"XML_Parse returned %1 error %2" ).arg( res ).arg( err ) );
 
  343    mError = 
QgsError( err, QStringLiteral( 
"GML schema" ) );
 
  344    mError.
append( tr( 
"Cannot guess schema" ) );
 
 
  350void QgsGmlSchema::startElement( 
const XML_Char *el, 
const XML_Char **attr )
 
  355  const QString elementName = QString::fromUtf8( el );
 
  356  QgsDebugMsgLevel( QStringLiteral( 
"-> %1 %2 %3" ).arg( mLevel ).arg( elementName, mLevel >= mSkipLevel ? 
"skip" : 
"" ), 5 );
 
  358  if ( mLevel >= mSkipLevel )
 
  364  mParsePathStack.append( elementName );
 
  365  const QString path = mParsePathStack.join( QLatin1Char( 
'.' ) );
 
  367  QStringList splitName = elementName.split( NS_SEPARATOR );
 
  368  const QString localName = splitName.last();
 
  369  const QString ns = splitName.size() > 1 ? splitName.first() : QString();
 
  372  const ParseMode parseMode = modeStackTop();
 
  375  if ( ns == 
GML_NAMESPACE && localName == QLatin1String( 
"boundedBy" ) )
 
  378    mSkipLevel = mLevel + 1;
 
  380  else if ( localName.compare( QLatin1String( 
"featureMembers" ), Qt::CaseInsensitive ) == 0 )
 
  382    mParseModeStack.push( QgsGmlSchema::FeatureMembers );
 
  389  else if ( localName.endsWith( QLatin1String( 
"member" ), Qt::CaseInsensitive ) )
 
  391    mParseModeStack.push( QgsGmlSchema::FeatureMember );
 
  394  else if ( elementName.endsWith( QLatin1String( 
"_layer" ) ) )
 
  402  else if ( elementName.endsWith( QLatin1String( 
"_feature" ) )
 
  403            || parseMode == QgsGmlSchema::FeatureMember
 
  404            || parseMode == QgsGmlSchema::FeatureMembers
 
  405            || localName.compare( QLatin1String( 
"feature" ), Qt::CaseInsensitive ) == 0 )
 
  408    if ( mFeatureClassMap.count( localName ) == 0 )
 
  412    mCurrentFeatureName = localName;
 
  413    mParseModeStack.push( QgsGmlSchema::Feature );
 
  415  else if ( parseMode == QgsGmlSchema::Attribute && ns == 
GML_NAMESPACE && mGeometryTypes.indexOf( localName ) >= 0 )
 
  418    QStringList &
geometryAttributes = mFeatureClassMap[mCurrentFeatureName].geometryAttributes();
 
  423    mSkipLevel = mLevel + 1; 
 
  425  else if ( parseMode == QgsGmlSchema::Feature )
 
  434    const QString name = readAttribute( QStringLiteral( 
"name" ), attr );
 
  436    if ( localName.compare( QLatin1String( 
"attribute" ), Qt::CaseInsensitive ) == 0
 
  439      const QString value = readAttribute( QStringLiteral( 
"value" ), attr );
 
  441      addAttribute( name, value );
 
  445      mAttributeName = localName;
 
  446      mParseModeStack.push( QgsGmlSchema::Attribute );
 
  452void QgsGmlSchema::endElement( 
const XML_Char *el )
 
  454  const QString elementName = QString::fromUtf8( el );
 
  455  QgsDebugMsgLevel( QStringLiteral( 
"<- %1 %2" ).arg( mLevel ).arg( elementName ), 5 );
 
  457  if ( mLevel >= mSkipLevel )
 
  466    mSkipLevel = std::numeric_limits<int>::max();
 
  469  QStringList splitName = elementName.split( NS_SEPARATOR );
 
  470  const QString localName = splitName.last();
 
  471  const QString ns = splitName.size() > 1 ? splitName.first() : QString();
 
  473  const QgsGmlSchema::ParseMode parseMode = modeStackTop();
 
  475  if ( parseMode == QgsGmlSchema::FeatureMembers )
 
  479  else if ( parseMode == QgsGmlSchema::Attribute && localName == mAttributeName )
 
  485    if ( mFeatureClassMap[mCurrentFeatureName].
geometryAttributes().count( mAttributeName ) == 0 )
 
  487      addAttribute( mAttributeName, mStringCash );
 
  490  else if ( ns == 
GML_NAMESPACE && localName == QLatin1String( 
"boundedBy" ) )
 
  494  else if ( localName.endsWith( QLatin1String( 
"member" ), Qt::CaseInsensitive ) )
 
  498  mParsePathStack.removeLast();
 
  502void QgsGmlSchema::characters( 
const XML_Char *chars, 
int len )
 
  505  if ( mLevel >= mSkipLevel )
 
  512  if ( modeStackTop() == QgsGmlSchema::Attribute )
 
  514    mStringCash.append( QString::fromUtf8( chars, len ) );
 
  518void QgsGmlSchema::addAttribute( 
const QString &name, 
const QString &value )
 
  522  ( void ) value.toInt( &ok );
 
  523  QMetaType::Type type = QMetaType::Type::QString;
 
  526    type = QMetaType::Type::Int;
 
  530    ( void ) value.toDouble( &ok );
 
  533      type = QMetaType::Type::Double;
 
  538  QList<QgsField> &
fields = mFeatureClassMap[mCurrentFeatureName].fields();
 
  539  const int fieldIndex = mFeatureClassMap[mCurrentFeatureName].fieldIndex( name );
 
  540  if ( fieldIndex == -1 )
 
  549    if ( ( field.
type() == QMetaType::Type::Int && ( type == QMetaType::Type::QString || type == QMetaType::Type::Double ) ) ||
 
  550         ( field.
type() == QMetaType::Type::Double && type == QMetaType::Type::QString ) )
 
  559  return mFeatureClassMap.keys();
 
 
  564  if ( mFeatureClassMap.count( 
typeName ) == 0 ) 
return QList<QgsField>();
 
  565  return mFeatureClassMap[
typeName].fields();
 
 
  570  if ( mFeatureClassMap.count( 
typeName ) == 0 ) 
return QStringList();
 
  571  return mFeatureClassMap[
typeName].geometryAttributes();
 
 
A container for error messages.
 
void append(const QString &message, const QString &tag)
Append new error message.
 
Encapsulate a field in an attribute table or data source.
 
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)