|
QGIS API Documentation
master-3f58142
|
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 }