Showing with 153 additions and 63 deletions.
  1. +142 −58 src/providers/wms/qgswmsprovider.cpp
  2. +11 −5 src/providers/wms/qgswmsprovider.h
200 changes: 142 additions & 58 deletions src/providers/wms/qgswmsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2566,28 +2566,6 @@ void QgsWmsProvider::parseLayer( QDomElement const & e, QgsWmsLayerProperty& lay
// can be combined with others later in supportedCrsForLayers()
mCrsForLayer[ layerProperty.name ] = layerProperty.crs;

// Store the WGS84 (CRS:84) extent so that it can be combined with others later
// in calculateExtent()

// Apply the coarse bounding box first
mExtentForLayer[ layerProperty.name ] = layerProperty.ex_GeographicBoundingBox;

// see if we can refine the bounding box with the CRS-specific bounding boxes
for ( int i = 0; i < layerProperty.boundingBox.size(); i++ )
{
QgsDebugMsg( "testing bounding box CRS which is "
+ layerProperty.boundingBox[i].crs + "." );

if ( layerProperty.boundingBox[i].crs == DEFAULT_LATLON_CRS )
{
mExtentForLayer[ layerProperty.name ] = layerProperty.boundingBox[i].box;
}
}

QgsDebugMsg( "extent for "
+ layerProperty.name + " is "
+ mExtentForLayer[ layerProperty.name ].toString( 3 ) + "." );

// Insert into the local class' registry
mLayersSupported.push_back( layerProperty );

Expand All @@ -2613,6 +2591,88 @@ void QgsWmsProvider::parseLayer( QDomElement const & e, QgsWmsLayerProperty& lay
QgsDebugMsg( "exiting." );
}


static const QgsWmsLayerProperty* _findNestedLayerProperty( const QString& layerName, const QgsWmsLayerProperty* prop )
{
if ( prop->name == layerName )
return prop;

foreach ( const QgsWmsLayerProperty& child, prop->layer )
{
if ( const QgsWmsLayerProperty* res = _findNestedLayerProperty( layerName, &child ) )
return res;
}

return 0;
}


bool QgsWmsProvider::extentForNonTiledLayer( const QString& layerName, const QString& crs, QgsRectangle& extent )
{
const QgsWmsLayerProperty* layerProperty = _findNestedLayerProperty( layerName, &mCapabilities.capability.layer );
if ( !layerProperty )
return false;

// see if we can refine the bounding box with the CRS-specific bounding boxes
for ( int i = 0; i < layerProperty->boundingBox.size(); i++ )
{
if ( layerProperty->boundingBox[i].crs == crs )
{
// exact bounding box is provided for this CRS
extent = layerProperty->boundingBox[i].box;
return true;
}
}

// exact bounding box for given CRS is not listed - we need to pick a different
// bounding box definition - either the coarse bounding box (in WGS84)
// or one of the alternative bounding box definitions for the layer

// Use the coarse bounding box
extent = layerProperty->ex_GeographicBoundingBox;

for ( int i = 0; i < layerProperty->boundingBox.size(); i++ )
{
if ( layerProperty->boundingBox[i].crs == DEFAULT_LATLON_CRS )
{
if ( layerProperty->boundingBox[i].box.contains( extent ) )
continue; // this bounding box is less specific (probably inherited from parent)

// this BBox is probably better than the one in ex_GeographicBoundingBox
extent = layerProperty->boundingBox[i].box;
break;
}
}

// transform it to requested CRS

QgsCoordinateReferenceSystem dst, wgs;
if ( !wgs.createFromOgcWmsCrs( DEFAULT_LATLON_CRS ) || !dst.createFromOgcWmsCrs( crs ) )
return false;

QgsCoordinateTransform xform( wgs, dst );
QgsDebugMsg( QString("transforming layer extent %1").arg( extent.toString( true ) ) );
try
{
extent = xform.transformBoundingBox( extent );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
return false;
}
QgsDebugMsg( QString("transformed layer extent %1").arg( extent.toString( true ) ) );

//make sure extent does not contain 'inf' or 'nan'
if ( !extent.isFinite() )
{
return false;
}

return true;
}


void QgsWmsProvider::parseTileSetProfile( QDomElement const &e )
{
QStringList resolutions, layers, styles;
Expand Down Expand Up @@ -2904,12 +2964,6 @@ void QgsWmsProvider::parseWMTSContents( QDomElement const &e )
}
}

if ( l.boundingBox.crs.isEmpty() )
{
l.boundingBox.box = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
l.boundingBox.crs = DEFAULT_LATLON_CRS;
}

for ( QDomElement e1 = e0.firstChildElement( "Style" );
!e1.isNull();
e1 = e1.nextSiblingElement( "Style" ) )
Expand Down Expand Up @@ -3110,6 +3164,61 @@ void QgsWmsProvider::parseWMTSContents( QDomElement const &e )
mTileThemes << QgsWmtsTheme();
parseTheme( e0, mTileThemes.back() );
}

// make sure that all layers have a bounding box
for( QList<QgsWmtsTileLayer>::iterator it = mTileLayersSupported.begin(); it != mTileLayersSupported.end(); ++it )
{
QgsWmtsTileLayer& l = *it;

if ( l.boundingBox.crs.isEmpty() )
{
if ( !detectTileLayerBoundingBox( l ) )
{
QgsDebugMsg( "failed to detect bounding box for " + l.identifier + " - using extent of the whole world" );
l.boundingBox.box = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
l.boundingBox.crs = DEFAULT_LATLON_CRS;
}
}
}
}


bool QgsWmsProvider::detectTileLayerBoundingBox( QgsWmtsTileLayer& l )
{
if ( l.setLinks.isEmpty() )
return false;

// take first supported tile matrix set
const QgsWmtsTileMatrixSetLink& setLink = l.setLinks.constBegin().value();

QHash<QString, QgsWmtsTileMatrixSet>::const_iterator tmsIt = mTileMatrixSets.constFind( setLink.tileMatrixSet );
if ( tmsIt == mTileMatrixSets.constEnd() )
return false;

QgsCoordinateReferenceSystem crs;
if ( !crs.createFromOgcWmsCrs( tmsIt->crs ) )
return false;

// take most coarse tile matrix ...
QMap<double, QgsWmtsTileMatrix>::const_iterator tmIt = tmsIt->tileMatrices.constEnd() - 1;
if ( tmIt == tmsIt->tileMatrices.constEnd() )
return false;

const QgsWmtsTileMatrix& tm = *tmIt;
double metersPerUnit = QGis::fromUnitToUnitFactor( crs.mapUnits(), QGis::Meters );
double res = tm.scaleDenom * 0.00028 / metersPerUnit;
QgsPoint bottomRight( tm.topLeft.x() + res * tm.tileWidth * tm.matrixWidth,
tm.topLeft.y() - res * tm.tileHeight * tm.matrixHeight );

QgsDebugMsg( QString( "detecting WMTS layer bounding box: tileset %1 matrix %2 crs %3 res %4" )
.arg( tmsIt->identifier ).arg( tm.identifier ).arg( tmsIt->crs ).arg( res ) );

QgsRectangle extent( tm.topLeft, bottomRight );
extent.normalize();

l.boundingBox.box = extent;
l.boundingBox.crs = tmsIt->crs;
return true;
}


Expand Down Expand Up @@ -3376,33 +3485,16 @@ bool QgsWmsProvider::calculateExtent()
++it )
{
QgsDebugMsg( "Sublayer iterator: " + *it );
// This is the extent for the layer name in *it
if ( !mExtentForLayer.contains( *it ) )
{
mLayerExtent = QgsRectangle();
appendError( ERR( tr( "Extent for layer %1 not found in capabilities" ).arg( *it ) ) );
continue;
}

QgsRectangle extent = mExtentForLayer.find( *it ).value();

// Convert to the user's CRS as required
try
{
extent = mCoordinateTransform->transformBoundingBox( extent, QgsCoordinateTransform::ForwardTransform );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
continue; //ignore extents of layers which cannot be transformed info the required CRS
}

//make sure extent does not contain 'inf' or 'nan'
if ( !extent.isFinite() )
QgsRectangle extent;
if ( !extentForNonTiledLayer( *it, mImageCrs, extent ) )
{
QgsDebugMsg( "extent for " + *it + " is invalid! (ignoring)" );
continue;
}

QgsDebugMsg( "extent for " + *it + " is " + extent.toString( 3 ) + "." );

// add to the combined extent of all the active sublayers
if ( firstLayer )
{
Expand Down Expand Up @@ -3572,14 +3664,6 @@ QString QgsWmsProvider::layerMetadata( QgsWmsLayerProperty &layer )
metadata += QString::number( layer.fixedHeight );
metadata += "</td></tr>";

// Layer Fixed Height
metadata += "<tr><td>";
metadata += tr( "WGS 84 Bounding Box" );
metadata += "</td>";
metadata += "<td>";
metadata += mExtentForLayer[ layer.name ].toString();
metadata += "</td></tr>";

// Layer Coordinate Reference Systems
for ( int j = 0; j < qMin( layer.crs.size(), 10 ); j++ )
{
Expand Down
16 changes: 11 additions & 5 deletions src/providers/wms/qgswmsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,17 @@ class QgsWmsProvider : public QgsRasterDataProvider
private:
void showMessageBox( const QString& title, const QString& text );

/**
* Try to get best extent for the layer in given CRS. Returns true on success, false otherwise (layer not found, invalid CRS, transform failed)
*/
bool extentForNonTiledLayer( const QString& layerName, const QString& crs, QgsRectangle& extent );

/**
* In case no bounding box is present in WMTS capabilities, try to estimate it from tile matrix sets.
* Returns true if the detection went fine.
*/
bool detectTileLayerBoundingBox( QgsWmtsTileLayer& l );

// case insensitive attribute value lookup
static QString nodeAttribute( const QDomElement &e, QString name, QString defValue = QString::null );

Expand Down Expand Up @@ -996,11 +1007,6 @@ class QgsWmsProvider : public QgsRasterDataProvider
*/
QList<QgsWmtsTheme> mTileThemes;

/**
* extents per layer (in WMS CRS:84 datum)
*/
QMap<QString, QgsRectangle> mExtentForLayer;

/**
* available CRSs per layer
*/
Expand Down