QGIS API Documentation  master-3f58142
src/core/qgsgmlschema.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002      qgsgmlschema.cpp
00003      --------------------------------------
00004     Date                 : February 2013
00005     Copyright            : (C) 2013 by Radim Blazek
00006     Email                : radim.blazek@gmail.com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 #include "qgsgmlschema.h"
00016 #include "qgsrectangle.h"
00017 #include "qgscoordinatereferencesystem.h"
00018 #include "qgsgeometry.h"
00019 #include "qgslogger.h"
00020 #include "qgsnetworkaccessmanager.h"
00021 #include <QBuffer>
00022 #include <QList>
00023 #include <QNetworkRequest>
00024 #include <QNetworkReply>
00025 #include <QProgressDialog>
00026 #include <QSet>
00027 #include <QSettings>
00028 #include <QUrl>
00029 
00030 #include <limits>
00031 
00032 const char NS_SEPARATOR = '?';
00033 const QString GML_NAMESPACE = "http://www.opengis.net/gml";
00034 
00035 QgsGmlFeatureClass::QgsGmlFeatureClass( )
00036 {
00037 }
00038 
00039 QgsGmlFeatureClass::QgsGmlFeatureClass( QString name, QString path )
00040     : mName( name )
00041     , mPath( path )
00042 {
00043 }
00044 
00045 QgsGmlFeatureClass::~QgsGmlFeatureClass()
00046 {
00047 }
00048 
00049 int QgsGmlFeatureClass::fieldIndex( const QString & name )
00050 {
00051   for ( int i = 0; i < mFields.size(); i++ )
00052   {
00053     if ( mFields[i].name() == name ) return i;
00054   }
00055   return -1;
00056 }
00057 
00058 // --------------------------- QgsGmlSchema -------------------------------
00059 QgsGmlSchema::QgsGmlSchema()
00060     : QObject()
00061 {
00062   mGeometryTypes << "Point" << "MultiPoint"
00063   << "LineString" << "MultiLineString"
00064   << "Polygon" << "MultiPolygon";
00065 }
00066 
00067 QgsGmlSchema::~QgsGmlSchema()
00068 {
00069 
00070 }
00071 
00072 QString QgsGmlSchema::readAttribute( const QString& attributeName, const XML_Char** attr ) const
00073 {
00074   int i = 0;
00075   while ( attr[i] != NULL )
00076   {
00077     if ( attributeName.compare( attr[i] ) == 0 )
00078     {
00079       return QString( attr[i+1] );
00080     }
00081     ++i;
00082   }
00083   return QString();
00084 }
00085 
00086 bool QgsGmlSchema::parseXSD( const QByteArray &xml )
00087 {
00088   QDomDocument dom;
00089   QString errorMsg;
00090   int errorLine;
00091   int errorColumn;
00092   if ( !dom.setContent( xml, false, &errorMsg, &errorLine, &errorColumn ) )
00093   {
00094     // TODO: error
00095     return false;
00096   }
00097 
00098   QDomElement docElem = dom.documentElement();
00099 
00100   QList<QDomElement> elementElements = domElements( docElem, "element" );
00101 
00102   //QgsDebugMsg( QString( "%1 elemets read" ).arg( elementElements.size() ) );
00103 
00104   foreach ( QDomElement elementElement, elementElements )
00105   {
00106     QString name = elementElement.attribute( "name" );
00107     QString type = elementElement.attribute( "type" );
00108 
00109     QString gmlBaseType = xsdComplexTypeGmlBaseType( docElem, stripNS( type ) );
00110     //QgsDebugMsg( QString( "gmlBaseType = %1" ).arg( gmlBaseType ) );
00111     //QgsDebugMsg( QString( "name = %1 gmlBaseType = %2" ).arg( name ).arg( gmlBaseType ) );
00112     // We should only use gml:AbstractFeatureType descendants which have
00113     // ancestor listed in gml:FeatureAssociationType (featureMember) descendant
00114     // But we could only loose some data if XSD was not correct, I think.
00115 
00116     if ( gmlBaseType == "AbstractFeatureType" )
00117     {
00118       // Get feature type definition
00119       QgsGmlFeatureClass featureClass( name, "" );
00120       xsdFeatureClass( docElem, stripNS( type ), featureClass );
00121       mFeatureClassMap.insert( name, featureClass );
00122     }
00123     // A feature may have more geometries, we take just the first one
00124   }
00125 
00126   return true;
00127 }
00128 
00129 bool QgsGmlSchema::xsdFeatureClass( const QDomElement &element, const QString & typeName, QgsGmlFeatureClass & featureClass )
00130 {
00131   //QgsDebugMsg("typeName = " + typeName );
00132   QDomElement complexTypeElement = domElement( element, "complexType", "name", typeName );
00133   if ( complexTypeElement.isNull() ) return false;
00134 
00135   // extension or restriction
00136   QDomElement extrest = domElement( complexTypeElement, "complexContent.extension" );
00137   if ( extrest.isNull() )
00138   {
00139     extrest = domElement( complexTypeElement, "complexContent.restriction" );
00140   }
00141   if ( extrest.isNull() ) return false;
00142 
00143   QString extrestName = extrest.attribute( "base" );
00144   if ( extrestName == "gml:AbstractFeatureType" )
00145   {
00146     // In theory we should add gml:AbstractFeatureType default attributes gml:description
00147     // and gml:name but it does not seem to be a common practice and we would probably
00148     // confuse most users
00149   }
00150   else
00151   {
00152     // Get attributes from extrest
00153     if ( !xsdFeatureClass( element, stripNS( extrestName ), featureClass ) ) return false;
00154   }
00155 
00156   // Supported geometry types
00157   QStringList geometryPropertyTypes;
00158   foreach ( QString geom, mGeometryTypes )
00159   {
00160     geometryPropertyTypes << geom + "PropertyType";
00161   }
00162 
00163   QStringList geometryAliases;
00164   geometryAliases << "location" << "centerOf" << "position" << "extentOf"
00165   << "coverage" << "edgeOf" << "centerLineOf" << "multiLocation"
00166   << "multiCenterOf" << "multiPosition" << "multiCenterLineOf"
00167   << "multiEdgeOf" << "multiCoverage" << "multiExtentOf";
00168 
00169   // Add attributes from current comple type
00170   QList<QDomElement> sequenceElements = domElements( extrest, "sequence.element" );
00171   foreach ( QDomElement sequenceElement, sequenceElements )
00172   {
00173     QString fieldName = sequenceElement.attribute( "name" );
00174     QString fieldTypeName = stripNS( sequenceElement.attribute( "type" ) );
00175     QString ref = sequenceElement.attribute( "ref" );
00176     //QgsDebugMsg ( QString("fieldName = %1 fieldTypeName = %2 ref = %3").arg(fieldName).arg(fieldTypeName).arg(ref) );
00177 
00178     if ( !ref.isEmpty() )
00179     {
00180       if ( ref.startsWith( "gml:" ) )
00181       {
00182         if ( geometryAliases.contains( stripNS( ref ) ) )
00183         {
00184           featureClass.geometryAttributes().append( stripNS( ref ) );
00185         }
00186         else
00187         {
00188           QgsDebugMsg( QString( "Unknown referenced GML element: %1" ).arg( ref ) );
00189         }
00190       }
00191       else
00192       {
00193         // TODO: get type from referenced element
00194         QgsDebugMsg( QString( "field %1.%2 is referencing %3 - not supported" ).arg( typeName ).arg( fieldName ) );
00195       }
00196       continue;
00197     }
00198 
00199     if ( fieldName.isEmpty() )
00200     {
00201       QgsDebugMsg( QString( "field in %1 without name" ).arg( typeName ) );
00202       continue;
00203     }
00204 
00205     // type is either type attribute
00206     if ( fieldTypeName.isEmpty() )
00207     {
00208       // or type is inheriting from xs:simpleType
00209       QDomElement sequenceElementRestriction = domElement( sequenceElement, "simpleType.restriction" );
00210       fieldTypeName = stripNS( sequenceElementRestriction.attribute( "base" ) );
00211     }
00212 
00213     QVariant::Type fieldType = QVariant::String;
00214     if ( fieldTypeName.isEmpty() )
00215     {
00216       QgsDebugMsg( QString( "Cannot get %1.%2 field type" ).arg( typeName ).arg( fieldName ) );
00217     }
00218     else
00219     {
00220       if ( geometryPropertyTypes.contains( fieldTypeName ) )
00221       {
00222         // Geometry attribute
00223         featureClass.geometryAttributes().append( fieldName );
00224         continue;
00225       }
00226 
00227       if ( fieldTypeName == "decimal" )
00228       {
00229         fieldType = QVariant::Double;
00230       }
00231       else if ( fieldTypeName == "integer" )
00232       {
00233         fieldType = QVariant::Int;
00234       }
00235     }
00236 
00237     QgsField field( fieldName, fieldType );
00238     featureClass.fields().append( field );
00239   }
00240 
00241   return true;
00242 }
00243 
00244 QString QgsGmlSchema::xsdComplexTypeGmlBaseType( const QDomElement &element, const QString & name )
00245 {
00246   //QgsDebugMsg("name = " + name );
00247   QDomElement complexTypeElement = domElement( element, "complexType", "name", name );
00248   if ( complexTypeElement.isNull() ) return "";
00249 
00250   QDomElement extrest = domElement( complexTypeElement, "complexContent.extension" );
00251   if ( extrest.isNull() )
00252   {
00253     extrest = domElement( complexTypeElement, "complexContent.restriction" );
00254   }
00255   if ( extrest.isNull() ) return "";
00256 
00257   QString extrestName = extrest.attribute( "base" );
00258   if ( extrestName.startsWith( "gml:" ) )
00259   {
00260     // GML base type found
00261     return stripNS( extrestName );
00262   }
00263   // Continue recursively until GML base type is reached
00264   return xsdComplexTypeGmlBaseType( element, stripNS( extrestName ) );
00265 }
00266 
00267 QString QgsGmlSchema::stripNS( const QString & name )
00268 {
00269   return name.contains( ":" ) ? name.section( ':', 1 ) : name;
00270 }
00271 
00272 QList<QDomElement> QgsGmlSchema::domElements( const QDomElement &element, const QString & path )
00273 {
00274   QList<QDomElement> list;
00275 
00276   QStringList names = path.split( "." );
00277   if ( names.size() == 0 ) return list;
00278   QString name = names.value( 0 );
00279   names.removeFirst();
00280 
00281   QDomNode n1 = element.firstChild();
00282   while ( !n1.isNull() )
00283   {
00284     QDomElement el = n1.toElement();
00285     if ( !el.isNull() )
00286     {
00287       QString tagName = stripNS( el.tagName() );
00288       if ( tagName == name )
00289       {
00290         if ( names.size() == 0 )
00291         {
00292           list.append( el );
00293         }
00294         else
00295         {
00296           list.append( domElements( el,  names.join( "." ) ) );
00297         }
00298       }
00299     }
00300     n1 = n1.nextSibling();
00301   }
00302 
00303   return list;
00304 }
00305 
00306 QDomElement QgsGmlSchema::domElement( const QDomElement &element, const QString & path )
00307 {
00308   return domElements( element, path ).value( 0 );
00309 }
00310 
00311 QList<QDomElement> QgsGmlSchema::domElements( QList<QDomElement> &elements, const QString & attr, const QString & attrVal )
00312 {
00313   QList<QDomElement> list;
00314   foreach ( QDomElement el, elements )
00315   {
00316     if ( el.attribute( attr ) == attrVal )
00317     {
00318       list << el;
00319     }
00320   }
00321   return list;
00322 }
00323 
00324 QDomElement QgsGmlSchema::domElement( const QDomElement &element, const QString & path, const QString & attr, const QString & attrVal )
00325 {
00326   QList<QDomElement> list = domElements( element, path );
00327   return domElements( list, attr, attrVal ).value( 0 );
00328 }
00329 
00330 bool QgsGmlSchema::guessSchema( const QByteArray &data )
00331 {
00332   QgsDebugMsg( "Entered" );
00333   mLevel = 0;
00334   mSkipLevel = std::numeric_limits<int>::max();
00335   XML_Parser p = XML_ParserCreateNS( NULL, NS_SEPARATOR );
00336   XML_SetUserData( p, this );
00337   XML_SetElementHandler( p, QgsGmlSchema::start, QgsGmlSchema::end );
00338   XML_SetCharacterDataHandler( p, QgsGmlSchema::chars );
00339   int atEnd = 1;
00340   XML_Parse( p, data.constData(), data.size(), atEnd );
00341   return 0;
00342 }
00343 
00344 void QgsGmlSchema::startElement( const XML_Char* el, const XML_Char** attr )
00345 {
00346   Q_UNUSED( attr );
00347   mLevel++;
00348 
00349   QString elementName( el );
00350   QgsDebugMsgLevel( QString( "-> %1 %2 %3" ).arg( mLevel ).arg( elementName ).arg( mLevel >= mSkipLevel ? "skip" : "" ), 5 );
00351 
00352   if ( mLevel >= mSkipLevel )
00353   {
00354     //QgsDebugMsg( QString("skip level %1").arg( mLevel ) );
00355     return;
00356   }
00357 
00358   mParsePathStack.append( elementName );
00359   QString path = mParsePathStack.join( "." );
00360 
00361   QStringList splitName =  elementName.split( NS_SEPARATOR );
00362   QString localName = splitName.last();
00363   QString ns = splitName.size() > 1 ? splitName.first() : "";
00364   //QgsDebugMsg( "ns = " + ns + " localName = " + localName );
00365 
00366   ParseMode parseMode = modeStackTop();
00367 
00368   if ( ns == GML_NAMESPACE && localName == "boundedBy" )
00369   {
00370     // gml:boundedBy in feature or feature collection -> skip
00371     mSkipLevel = mLevel + 1;
00372   }
00373   // GML does not specify that gml:FeatureAssociationType elements should end
00374   // with 'Member' apart standard gml:featureMember, but it is quite usual to
00375   // that the names ends with 'Member', e.g.: osgb:topographicMember, cityMember,...
00376   // so this is really fail if the name does not contain 'Member'
00377   else if ( localName.endsWith( "member", Qt::CaseInsensitive ) )
00378   {
00379     mParseModeStack.push( QgsGmlSchema::featureMember );
00380   }
00381   // UMN Mapserver simple GetFeatureInfo response layer element (ends with _layer)
00382   else if ( elementName.endsWith( "_layer" ) )
00383   {
00384     // do nothing, we catch _feature children
00385   }
00386   // UMN Mapserver simple GetFeatureInfo response feature element (ends with _feature)
00387   // or featureMember children
00388   else if ( elementName.endsWith( "_feature" )
00389             || parseMode == QgsGmlSchema::featureMember )
00390   {
00391     //QgsDebugMsg ( "is feature path = " + path );
00392     if ( mFeatureClassMap.count( localName ) == 0 )
00393     {
00394       mFeatureClassMap.insert( localName, QgsGmlFeatureClass( localName, path ) );
00395     }
00396     mCurrentFeatureName = localName;
00397     mParseModeStack.push( QgsGmlSchema::feature );
00398   }
00399   else if ( parseMode == QgsGmlSchema::attribute && ns == GML_NAMESPACE && mGeometryTypes.indexOf( localName ) >= 0 )
00400   {
00401     // Geometry (Point,MultiPoint,...) in geometry attribute
00402     QStringList &geometryAttributes = mFeatureClassMap[mCurrentFeatureName].geometryAttributes();
00403     if ( geometryAttributes.count( mAttributeName ) == 0 )
00404     {
00405       geometryAttributes.append( mAttributeName );
00406     }
00407     mSkipLevel = mLevel + 1; // no need to parse children
00408   }
00409   else if ( parseMode == QgsGmlSchema::feature )
00410   {
00411     // An element in feature should be ordinary or geometry attribute
00412     //QgsDebugMsg( "is attribute");
00413     mParseModeStack.push( QgsGmlSchema::attribute );
00414     mAttributeName = localName;
00415     mStringCash.clear();
00416   }
00417 }
00418 
00419 void QgsGmlSchema::endElement( const XML_Char* el )
00420 {
00421   QString elementName( el );
00422   QgsDebugMsgLevel( QString( "<- %1 %2" ).arg( mLevel ).arg( elementName ), 5 );
00423 
00424   if ( mLevel >= mSkipLevel )
00425   {
00426     //QgsDebugMsg( QString("skip level %1").arg( mLevel ) );
00427     mLevel--;
00428     return;
00429   }
00430   else
00431   {
00432     // clear possible skip level
00433     mSkipLevel = std::numeric_limits<int>::max();
00434   }
00435 
00436   QStringList splitName =  elementName.split( NS_SEPARATOR );
00437   QString localName = splitName.last();
00438   QString ns = splitName.size() > 1 ? splitName.first() : "";
00439 
00440   QgsGmlSchema::ParseMode parseMode = modeStackTop();
00441 
00442   if ( parseMode == QgsGmlSchema::attribute && localName == mAttributeName )
00443   {
00444     // End of attribute
00445     //QgsDebugMsg("end attribute");
00446     modeStackPop(); // go up to feature
00447 
00448     if ( mFeatureClassMap[mCurrentFeatureName].geometryAttributes().count( mAttributeName ) == 0 )
00449     {
00450       // It is not geometry attribute -> analyze value
00451       bool ok;
00452       mStringCash.toInt( &ok );
00453       QVariant::Type type = QVariant::String;
00454       if ( ok )
00455       {
00456         type = QVariant::Int;
00457       }
00458       else
00459       {
00460         mStringCash.toDouble( &ok );
00461         if ( ok )
00462         {
00463           type = QVariant::Double;
00464         }
00465       }
00466       //QgsDebugMsg( "mStringCash = " + mStringCash + " type = " + QVariant::typeToName( type )  );
00467       //QMap<QString, QgsField> & fields = mFeatureClassMap[mCurrentFeatureName].fields();
00468       QList<QgsField> & fields = mFeatureClassMap[mCurrentFeatureName].fields();
00469       int fieldIndex = mFeatureClassMap[mCurrentFeatureName].fieldIndex( mAttributeName );
00470       if ( fieldIndex == -1 )
00471       {
00472         QgsField field( mAttributeName, type );
00473         fields.append( field );
00474       }
00475       else
00476       {
00477         QgsField &field = fields[fieldIndex];
00478         // check if type is sufficient
00479         if (( field.type() == QVariant::Int && ( type == QVariant::String || type == QVariant::Double ) ) ||
00480             ( field.type() == QVariant::Double && type == QVariant::String ) )
00481         {
00482           field.setType( type );
00483         }
00484       }
00485     }
00486   }
00487   else if ( ns == GML_NAMESPACE && localName == "boundedBy" )
00488   {
00489     // was skipped
00490   }
00491   else if ( localName.endsWith( "member", Qt::CaseInsensitive ) )
00492   {
00493     mParseModeStack.push( QgsGmlSchema::featureMember );
00494     modeStackPop();
00495   }
00496   mParsePathStack.removeLast();
00497   mLevel--;
00498 }
00499 
00500 void QgsGmlSchema::characters( const XML_Char* chars, int len )
00501 {
00502   //QgsDebugMsg( QString("level %1 : %2").arg( mLevel ).arg( QString::fromUtf8( chars, len ) ) );
00503   if ( mLevel >= mSkipLevel )
00504   {
00505     //QgsDebugMsg( QString("skip level %1").arg( mLevel ) );
00506     return;
00507   }
00508 
00509   //save chars in mStringCash attribute mode for value type analysis
00510   if ( modeStackTop() == QgsGmlSchema::attribute )
00511   {
00512     mStringCash.append( QString::fromUtf8( chars, len ) );
00513   }
00514 }
00515 
00516 QStringList QgsGmlSchema::typeNames() const
00517 {
00518   return mFeatureClassMap.keys();
00519 }
00520 
00521 QList<QgsField> QgsGmlSchema::fields( const QString & typeName )
00522 {
00523   if ( mFeatureClassMap.count( typeName ) == 0 ) return QList<QgsField>();
00524   return mFeatureClassMap[typeName].fields();
00525 }
00526 
00527 QStringList QgsGmlSchema::geometryAttributes( const QString & typeName )
00528 {
00529   if ( mFeatureClassMap.count( typeName ) == 0 ) return QStringList();
00530   return mFeatureClassMap[typeName].geometryAttributes();
00531 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines