From 8cde0d3e0f976ea35bc9536ba9e16f70de4fecb8 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Mon, 7 May 2012 22:46:54 +0200 Subject: [PATCH] WMTS/WMS updates: - larger precision in BBOX (WMS/WMS-C) - keep GetCapabilities request or document for GetCapabilities request (WMTS) - parse multiple image format and infoFormat elements in GetCapabilities response - support diffent nestings of TileMatrixSetLink/TileMatrixSet - case insesitive parsing of ResourceURL attributes --- src/providers/wms/qgswmsprovider.cpp | 138 ++++++++++++++--------- src/providers/wms/qgswmsprovider.h | 7 +- src/providers/wms/qgswmssourceselect.cpp | 48 ++++---- 3 files changed, 115 insertions(+), 78 deletions(-) diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 7d30a1ec5f99..3144a6a42883 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -234,6 +234,11 @@ void QgsWmsProvider::parseUri( QString uri ) QString QgsWmsProvider::prepareUri( QString uri ) const { + if ( uri.contains( "SERVICE=WMTS" ) || uri.contains( "/WMTSCapabilities.xml" ) ) + { + return uri; + } + if ( !uri.contains( "?" ) ) { uri.append( "?" ); @@ -548,10 +553,10 @@ QImage *QgsWmsProvider::draw( QgsRectangle const &viewExtent, int pixelWidth, i // Bounding box in WMS format (Warning: does not work with scientific notation) QString bbox = QString( changeXY ? "%2,%1,%4,%3" : "%1,%2,%3,%4" ) - .arg( viewExtent.xMinimum(), 0, 'f' ) - .arg( viewExtent.yMinimum(), 0, 'f' ) - .arg( viewExtent.xMaximum(), 0, 'f' ) - .arg( viewExtent.yMaximum(), 0, 'f' ); + .arg( viewExtent.xMinimum(), 0, 'f', 16 ) + .arg( viewExtent.yMinimum(), 0, 'f', 16 ) + .arg( viewExtent.xMaximum(), 0, 'f', 16 ) + .arg( viewExtent.yMaximum(), 0, 'f', 16 ); mCachedImage = new QImage( pixelWidth, pixelHeight, QImage::Format_ARGB32 ); mCachedImage->fill( 0 ); @@ -702,7 +707,6 @@ QImage *QgsWmsProvider::draw( QgsRectangle const &viewExtent, int pixelWidth, i double thMap = tm->tileHeight * tres; QgsDebugMsg( QString( "tile map size: %1,%2" ).arg( twMap, 0, 'f' ).arg( thMap, 0, 'f' ) ); - int minTileCol = 0; int maxTileCol = tm->matrixWidth - 1; int minTileRow = 0; @@ -759,10 +763,10 @@ QImage *QgsWmsProvider::draw( QgsRectangle const &viewExtent, int pixelWidth, i QString turl; turl += url.toString(); turl += QString( changeXY ? "&BBOX=%2,%1,%4,%3" : "&BBOX=%1,%2,%3,%4" ) - .arg( tm->topLeft.x() + col * twMap, 0, 'f' ) - .arg( tm->topLeft.y() - ( row + 1 ) * thMap, 0, 'f' ) - .arg( tm->topLeft.x() + ( col + 1 ) * twMap, 0, 'f' ) - .arg( tm->topLeft.y() - row * thMap, 0, 'f' ); + .arg( tm->topLeft.x() + col * twMap /* + twMap * 0.001 */, 0, 'f', 16 ) + .arg( tm->topLeft.y() - ( row + 1 ) * thMap /* - thMap * 0.001 */, 0, 'f', 16 ) + .arg( tm->topLeft.x() + ( col + 1 ) * twMap /* - twMap * 0.001 */, 0, 'f', 16 ) + .arg( tm->topLeft.y() - row * thMap /* + thMap * 0.001 */, 0, 'f', 16 ); QNetworkRequest request( turl ); setAuthorization( request ); @@ -964,17 +968,19 @@ void QgsWmsProvider::tileReplyFinished() QRectF r = reply->request().attribute( static_cast( QNetworkRequest::User + 2 ) ).toRectF(); #if QT_VERSION >= 0x40500 - QgsDebugMsg( QString( "tile reply %1 (%2) tile:%3 rect:%4,%5 %6x%7) fromcache:%8 error:%9" ) + QgsDebugMsg( QString( "tile reply %1 (%2) tile:%3 rect:%4,%5 %6x%7) fromcache:%8 error:%9 url:%10" ) .arg( tileReqNo ).arg( mTileReqNo ).arg( tileNo ) .arg( r.left(), 0, 'f' ).arg( r.bottom(), 0, 'f' ).arg( r.width(), 0, 'f' ).arg( r.height(), 0, 'f' ) .arg( fromCache ) .arg( reply->errorString() ) + .arg( reply->url().toString() ) ); #else - QgsDebugMsg( QString( "tile reply %1 (%2) tile:%3 rect:%4,%5 %6x%7) error:%8" ) + QgsDebugMsg( QString( "tile reply %1 (%2) tile:%3 rect:%4,%5 %6x%7) error:%8 url:%9" ) .arg( tileReqNo ).arg( mTileReqNo ).arg( tileNo ) .arg( r.left(), 0, 'f' ).arg( r.bottom(), 0, 'f' ).arg( r.width(), 0, 'f' ).arg( r.height(), 0, 'f' ) .arg( reply->errorString() ) + .arg( reply->url().toString() ) ); #endif @@ -1202,7 +1208,12 @@ bool QgsWmsProvider::retrieveServerCapabilities( bool forceRefresh ) if ( mHttpCapabilitiesResponse.isNull() || forceRefresh ) { - QString url = mBaseUrl + "SERVICE=WMS&REQUEST=GetCapabilities"; + QString url = mBaseUrl; + if ( !url.contains( "SERVICE=WMTS" ) && + !url.contains( "/WMTSCapabilities.xml" ) ) + { + url += "SERVICE=WMS&REQUEST=GetCapabilities"; + } mError = ""; @@ -2317,7 +2328,7 @@ void QgsWmsProvider::parseTileSetProfile( QDomElement const &e ) } else if ( tagName == "Format" ) { - l.format = e1.text(); + l.formats << e1.text(); } else if ( tagName == "BoundingBox" ) { @@ -2574,12 +2585,17 @@ void QgsWmsProvider::parseWMTSContents( QDomElement const &e ) l.defaultStyle = s.identifier; } - l.format = e0.firstChildElement( "Format" ).text(); - l.infoFormat = e0.firstChildElement( "InfoFormat" ).text(); + for ( QDomElement e1 = e0.firstChildElement( "Format" ); !e1.isNull(); e1 = e1.nextSiblingElement( "Format" ) ) + { + l.formats << e1.text(); + } + + for ( QDomElement e1 = e0.firstChildElement( "InfoFormat" ); !e1.isNull(); e1 = e1.nextSiblingElement( "InfoFormat" ) ) + { + l.infoFormats << e1.text(); + } - for ( QDomElement e1 = e0.firstChildElement( "Dimension" ); - !e1.isNull(); - e1 = e1.nextSiblingElement( "Dimension" ) ) + for ( QDomElement e1 = e0.firstChildElement( "Dimension" ); !e1.isNull(); e1 = e1.nextSiblingElement( "Dimension" ) ) { QgsWmtsDimension d; @@ -2603,49 +2619,49 @@ void QgsWmsProvider::parseWMTSContents( QDomElement const &e ) l.dimensions.insert( d.identifier, d ); } - for ( QDomElement e1 = e0.firstChildElement( "TileMatrixSetLink" ).firstChildElement( "TileMatrixSet" ); - !e1.isNull(); - e1 = e1.nextSiblingElement( "TileMatrixSet" ) ) + for ( QDomElement e1 = e0.firstChildElement( "TileMatrixSetLink" ); !e1.isNull(); e1 = e1.nextSiblingElement( "TileMatrixSetLink" ) ) { - QgsWmtsTileMatrixSetLink sl; - - sl.tileMatrixSet = e1.text(); - if ( !mTileMatrixSets.contains( sl.tileMatrixSet ) ) + for ( QDomElement e2 = e1.firstChildElement( "TileMatrixSet" ); !e2.isNull(); e2 = e2.nextSiblingElement( "TileMatrixSet" ) ) { - QgsDebugMsg( QString( "tileMatrixSet %1 not found." ).arg( id ) ); - continue; - } + QgsWmtsTileMatrixSetLink sl; - for ( QDomElement e2 = e1.firstChildElement( "TileMatrixSetLimits" ).firstChildElement( "TileMatrixLimits" ); - !e2.isNull(); - e2 = e2.nextSiblingElement( "TileMatrixLimits" ) ) - { - QgsWmtsTileMatrixLimits limit; + sl.tileMatrixSet = e2.text(); + if ( !mTileMatrixSets.contains( sl.tileMatrixSet ) ) + { + QgsDebugMsg( QString( "tileMatrixSet %1 not found." ).arg( id ) ); + continue; + } - QString id = e2.firstChildElement( "TileMatrix" ).text(); + for ( QDomElement e3 = e2.firstChildElement( "TileMatrixSetLimits" ); !e3.isNull(); e3 = e3.nextSiblingElement( "TileMatrixSetLimits" ) ) + { + for ( QDomElement e4 = e3.firstChildElement( "TileMatrixLimits" ); !e4.isNull(); e4 = e4.nextSiblingElement( "TileMatrixLimits" ) ) + { + QgsWmtsTileMatrixLimits limit; - limit.minTileRow = e2.firstChildElement( "MinTileRow" ).text().toInt(); - limit.maxTileRow = e2.firstChildElement( "MaxTileRow" ).text().toInt(); - limit.minTileCol = e2.firstChildElement( "MinTileCol" ).text().toInt(); - limit.maxTileCol = e2.firstChildElement( "MaxTileCol" ).text().toInt(); + QString id = e4.firstChildElement( "TileMatrix" ).text(); - sl.limits.insert( id, limit ); - } + limit.minTileRow = e4.firstChildElement( "MinTileRow" ).text().toInt(); + limit.maxTileRow = e4.firstChildElement( "MaxTileRow" ).text().toInt(); + limit.minTileCol = e4.firstChildElement( "MinTileCol" ).text().toInt(); + limit.maxTileCol = e4.firstChildElement( "MaxTileCol" ).text().toInt(); - l.setLinks.insert( id, sl ); + sl.limits.insert( id, limit ); + } + } + + l.setLinks.insert( sl.tileMatrixSet, sl ); + } } - for ( QDomElement e1 = e0.firstChildElement( "ResourceURL" ); - !e1.isNull(); - e1 = e1.nextSiblingElement( "ResourceURL" ) ) + for ( QDomElement e1 = e0.firstChildElement( "ResourceURL" ); !e1.isNull(); e1 = e1.nextSiblingElement( "ResourceURL" ) ) { - QString format = e1.attribute( "format" ); - QString resourceType = e1.attribute( "resourceType" ); - QString tmpl = e1.attribute( "template" ); + QString format = nodeAttribute( e1, "format" ); + QString resourceType = nodeAttribute( e1, "resourceType" ); + QString tmpl = nodeAttribute( e1, "template" ); if ( format.isEmpty() || resourceType.isEmpty() || tmpl.isEmpty() ) { - QgsDebugMsg( QString( "SKIPPING ResourcURL format=%1 resourceType=%2 template=%3" ) + QgsDebugMsg( QString( "SKIPPING ResourceURL format=%1 resourceType=%2 template=%3" ) .arg( format ) .arg( resourceType ) .arg( tmpl ) ) ; @@ -3614,10 +3630,10 @@ QStringList QgsWmsProvider::identifyAs( const QgsPoint& point, QString format ) // Compose request to WMS server QString bbox = QString( changeXY ? "%2,%1,%4,%3" : "%1,%2,%3,%4" ) - .arg( mCachedViewExtent.xMinimum(), 0, 'f' ) - .arg( mCachedViewExtent.yMinimum(), 0, 'f' ) - .arg( mCachedViewExtent.xMaximum(), 0, 'f' ) - .arg( mCachedViewExtent.yMaximum(), 0, 'f' ); + .arg( mCachedViewExtent.xMinimum(), 0, 'f', 16 ) + .arg( mCachedViewExtent.yMinimum(), 0, 'f', 16 ) + .arg( mCachedViewExtent.xMaximum(), 0, 'f', 16 ) + .arg( mCachedViewExtent.yMaximum(), 0, 'f', 16 ); // Test for which layers are suitable for querying with for ( QStringList::const_iterator @@ -3819,7 +3835,6 @@ QVector QgsWmsProvider::supportedFormats() QVector formats; QStringList mFormats, mLabels; - QList supportedFormats = QImageReader::supportedImageFormats(); if ( supportedFormats.contains( "png" ) ) @@ -3855,6 +3870,22 @@ QVector QgsWmsProvider::supportedFormats() return formats; } +QString QgsWmsProvider::nodeAttribute( const QDomElement &e, QString name, QString defValue ) +{ + if ( e.hasAttribute( name ) ) + return e.attribute( name ); + + QDomNamedNodeMap map( e.attributes() ); + for ( int i = 0; i < map.size(); i++ ) + { + QDomAttr attr( map.item( i ).toElement().toAttr() ); + if ( attr.name().compare( name, Qt::CaseInsensitive ) == 0 ) + return attr.value(); + } + + return defValue; +} + void QgsWmsProvider::showMessageBox( const QString& title, const QString& text ) { QgsMessageOutput *message = QgsMessageOutput::createMessageOutput(); @@ -3892,4 +3923,3 @@ QGISEXTERN bool isProvider() { return true; } - diff --git a/src/providers/wms/qgswmsprovider.h b/src/providers/wms/qgswmsprovider.h index d167f6efd572..2c5988350690 100644 --- a/src/providers/wms/qgswmsprovider.h +++ b/src/providers/wms/qgswmsprovider.h @@ -392,8 +392,8 @@ struct QgsWmtsTileLayer QString title, abstract; QStringList keywords; QgsWmsBoundingBoxProperty boundingBox; - QString format; - QString infoFormat; + QStringList formats; + QStringList infoFormats; QString defaultStyle; QHash dimensions; QHash styles; @@ -772,6 +772,9 @@ class QgsWmsProvider : public QgsRasterDataProvider private: void showMessageBox( const QString& title, const QString& text ); + // case insensitive attribute value lookup + static QString nodeAttribute( const QDomElement &e, QString name, QString defValue = QString::null ); + /** * \brief Retrieve and parse the (cached) Capabilities document from the server * diff --git a/src/providers/wms/qgswmssourceselect.cpp b/src/providers/wms/qgswmssourceselect.cpp index c647a690e33d..da2c0b142ea4 100644 --- a/src/providers/wms/qgswmssourceselect.cpp +++ b/src/providers/wms/qgswmssourceselect.cpp @@ -362,30 +362,34 @@ bool QgsWMSSourceSelect::populateLayerList( QgsWmsProvider *wmsProvider ) { foreach( const QgsWmtsTileMatrixSetLink &setLink, l.setLinks ) { - QTableWidgetItem *item = new QTableWidgetItem( l.identifier ); - item->setData( Qt::UserRole + 0, l.identifier ); - item->setData( Qt::UserRole + 1, l.format ); - item->setData( Qt::UserRole + 2, style.identifier ); - item->setData( Qt::UserRole + 3, setLink.tileMatrixSet ); - item->setData( Qt::UserRole + 4, tileMatrixSets[ setLink.tileMatrixSet ].crs ); - - lstTilesets->setItem( row, 0, item ); - lstTilesets->setItem( row, 1, new QTableWidgetItem( l.format ) ); - lstTilesets->setItem( row, 2, new QTableWidgetItem( style.identifier ) ); - QTableWidgetItem *styleItem = new QTableWidgetItem( l.title ); - if ( !l.abstract.isEmpty() ) - styleItem->setToolTip( "

" + l.abstract + "

" ); - lstTilesets->setItem( row, 3, styleItem ); - lstTilesets->setItem( row, 4, new QTableWidgetItem( setLink.tileMatrixSet ) ); - lstTilesets->setItem( row, 5, new QTableWidgetItem( tileMatrixSets[ setLink.tileMatrixSet ].crs ) ); - - if ( !mMimeMap.contains( l.format ) ) + foreach( QString format, l.formats ) { - for ( int i = 0; i < lstTilesets->columnCount(); i++ ) + QTableWidgetItem *item = new QTableWidgetItem( l.identifier ); + item->setData( Qt::UserRole + 0, l.identifier ); + + item->setData( Qt::UserRole + 1, format ); + item->setData( Qt::UserRole + 2, style.identifier ); + item->setData( Qt::UserRole + 3, setLink.tileMatrixSet ); + item->setData( Qt::UserRole + 4, tileMatrixSets[ setLink.tileMatrixSet ].crs ); + + lstTilesets->setItem( row, 0, item ); + lstTilesets->setItem( row, 1, new QTableWidgetItem( format ) ); + lstTilesets->setItem( row, 2, new QTableWidgetItem( style.identifier ) ); + QTableWidgetItem *styleItem = new QTableWidgetItem( l.title ); + if ( !l.abstract.isEmpty() ) + styleItem->setToolTip( "

" + l.abstract + "

" ); + lstTilesets->setItem( row, 3, styleItem ); + lstTilesets->setItem( row, 4, new QTableWidgetItem( setLink.tileMatrixSet ) ); + lstTilesets->setItem( row, 5, new QTableWidgetItem( tileMatrixSets[ setLink.tileMatrixSet ].crs ) ); + + if ( !mMimeMap.contains( format ) ) { - QTableWidgetItem *item = lstTilesets->item( row, i ); - item->setFlags( item->flags() & ~Qt::ItemIsEnabled ); - item->setToolTip( tr( "encoding %1 not supported." ).arg( l.format ) ); + for ( int i = 0; i < lstTilesets->columnCount(); i++ ) + { + QTableWidgetItem *item = lstTilesets->item( row, i ); + item->setFlags( item->flags() & ~Qt::ItemIsEnabled ); + item->setToolTip( tr( "encoding %1 not supported." ).arg( format ) ); + } } }