[FEATURE] Native support for XYZ tile layers

Yes, that means OpenStreetMap tiles and various other sources!

No GUI so far...
This commit is contained in:
Martin Dobias 2016-08-31 15:32:13 +08:00
parent 0c51c3f08d
commit c4181fa04d
4 changed files with 161 additions and 21 deletions

View File

@ -40,6 +40,37 @@ bool QgsWmsSettings::parseUri( const QString& uriString )
QgsDataSourceUri uri;
uri.setEncodedUri( uriString );
mXyz = false; // assume WMS / WMTS
if ( uri.param( "type" ) == "xyz" )
{
// for XYZ tiles most of the things do not apply
mTiled = true;
mXyz = true;
mTileDimensionValues.clear();
mTileMatrixSetId = "tms0";
mMaxWidth = 0;
mMaxHeight = 0;
mHttpUri = uri.param( "url" );
mBaseUrl = mHttpUri;
mAuth.mUserName.clear();
mAuth.mPassword.clear();
mAuth.mReferer.clear();
mAuth.mAuthCfg.clear();
mIgnoreGetMapUrl = false;
mIgnoreGetFeatureInfoUrl = false;
mSmoothPixmapTransform = false;
mDpiMode = dpiNone; // does not matter what we set here
mActiveSubLayers = QStringList( "xyz" ); // just a placeholder to have one sub-layer
mActiveSubStyles = QStringList( "xyz" ); // just a placeholder to have one sub-style
mActiveSubLayerVisibility.clear();
mFeatureCount = 0;
mImageMimeType.clear();
mCrsId = "EPSG:3857";
mEnableContextualLegend = false;
return true;
}
mTiled = false;
mTileDimensionValues.clear();

View File

@ -339,18 +339,20 @@ struct QgsWmtsTileMatrix
struct QgsWmtsTileMatrixSet
{
QString identifier;
QString title, abstract;
QStringList keywords;
QString crs;
QString wkScaleSet;
QString identifier; //!< tile matrix set identifier
QString title; //!< human readable tile matrix set name
QString abstract; //!< brief description of the tile matrix set
QStringList keywords; //!< list of words/phrases to describe the dataset
QString crs; //!< CRS of the tile matrix set
QString wkScaleSet; //!< optional reference to a well-known scale set
//! available tile matrixes (key = pixel span in map units)
QMap<double, QgsWmtsTileMatrix> tileMatrices;
//! Returns closest tile resolution to the requested one. (resolution = width [map units] / with [pixels])
const QgsWmtsTileMatrix* findNearestResolution( double vres ) const;
};
enum QgsTileMode { WMTS, WMSC };
enum QgsTileMode { WMTS, WMSC, XYZ };
struct QgsWmtsTileMatrixLimits
{
@ -382,16 +384,22 @@ struct QgsWmtsStyle
QList<QgsWmtsLegendURL> legendURLs;
};
/**
* In case of multi-dimensional data, the service metadata can describe their multi-
* dimensionality and tiles can be requested at specific values in these dimensions.
* Examples of dimensions are Time, Elevation and Band.
*/
struct QgsWmtsDimension
{
QString identifier;
QString title, abstract;
QStringList keywords;
QString UOM;
QString unitSymbol;
QString defaultValue;
bool current;
QStringList values;
QString identifier; //!< name of the dimensional axis
QString title; //!< human readable name
QString abstract; //!< brief description of the dimension
QStringList keywords; //!< list of words/phrases to describe the dataset
QString UOM; //!< units of measure of dimensional axis
QString unitSymbol; //!< symbol of the units
QString defaultValue; //!< default value to be used if value is not specified in request
bool current; //!< indicates whether temporal data are normally kept current
QStringList values; //!< available values for this dimension
};
struct QgsWmtsTileLayer
@ -404,6 +412,7 @@ struct QgsWmtsTileLayer
QStringList formats;
QStringList infoFormats;
QString defaultStyle;
//! available dimensions (optional, for multi-dimensional data)
QHash<QString, QgsWmtsDimension> dimensions;
QHash<QString, QgsWmtsStyle> styles;
QHash<QString, QgsWmtsTileMatrixSetLink> setLinks;
@ -532,7 +541,11 @@ class QgsWmsSettings
//! layer is tiled, tile layer and active matrix set
bool mTiled;
//! whether we actually work with XYZ tiles instead of WMS / WMTS
bool mXyz;
//! chosen values for dimensions in case of multi-dimensional data (key=dim id, value=dim value)
QHash<QString, QString> mTileDimensionValues;
//! name of the chosen tile matrix set
QString mTileMatrixSetId;
/**

View File

@ -79,6 +79,7 @@ static QString DEFAULT_LATLON_CRS = "CRS:84";
QMap<QString, QgsWmsStatistics::Stat> QgsWmsStatistics::sData;
QgsWmsProvider::QgsWmsProvider( QString const& uri, const QgsWmsCapabilities* capabilities )
: QgsRasterDataProvider( uri )
, mHttpGetLegendGraphicResponse( nullptr )
@ -110,14 +111,26 @@ QgsWmsProvider::QgsWmsProvider( QString const& uri, const QgsWmsCapabilities* ca
if ( !addLayers() )
return;
// if there are already parsed capabilities, use them!
if ( capabilities )
mCaps = *capabilities;
// Make sure we have capabilities - other functions here may need them
if ( !retrieveServerCapabilities() )
if ( mSettings.mXyz )
{
return;
// we are working with XYZ tiles
// no need to get capabilities, the whole definition is in URI
// so we just generate a dummy WMTS definition
setupXyzCapabilities( uri );
}
else
{
// we are working with WMS / WMTS server
// if there are already parsed capabilities, use them!
if ( capabilities )
mCaps = *capabilities;
// Make sure we have capabilities - other functions here may need them
if ( !retrieveServerCapabilities() )
{
return;
}
}
// setImageCrs is using mTiled !!!
@ -781,6 +794,28 @@ QImage *QgsWmsProvider::draw( QgsRectangle const & viewExtent, int pixelWidth, i
}
break;
case XYZ:
{
QString url = mSettings.mBaseUrl;
int z = tm->identifier.toInt();
int i = 0;
for ( int row = row0; row <= row1; row++ )
{
for ( int col = col0; col <= col1; col++ )
{
QString turl( url );
turl.replace( "{x}", QString::number( col ), Qt::CaseInsensitive );
turl.replace( "{y}", QString::number( row ), Qt::CaseInsensitive );
turl.replace( "{z}", QString::number( z ), Qt::CaseInsensitive );
if ( feedback && !feedback->preview_only )
QgsDebugMsg( QString( "tileRequest %1 %2/%3 (%4,%5): %6" ).arg( mTileReqNo ).arg( i++ ).arg( n ).arg( row ).arg( col ).arg( turl ) );
requests << QgsWmsTiledImageDownloadHandler::TileRequest( turl, tm->tileRect( col, row ), i );
}
}
}
break;
default:
QgsDebugMsg( QString( "unexpected tile mode %1" ).arg( mTileLayer->tileMode ) );
return image;
@ -935,6 +970,60 @@ bool QgsWmsProvider::retrieveServerCapabilities( bool forceRefresh )
}
void QgsWmsProvider::setupXyzCapabilities( const QString &uri )
{
QgsDataSourceUri parsedUri;
parsedUri.setEncodedUri( uri );
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( "EPSG:4326" ), QgsCoordinateReferenceSystem( mSettings.mCrsId ) );
// the whole world is projected to a square:
// X going from 180 W to 180 E
// Y going from ~85 N to ~85 S (=atan(sinh(pi)) ... to get a square)
QgsPoint topLeftLonLat( -180, 180.0 / M_PI * atan( sinh( M_PI ) ) );
QgsPoint bottomRightLonLat( 180, 180.0 / M_PI * atan( sinh( -M_PI ) ) );
QgsPoint topLeft = ct.transform( topLeftLonLat );
QgsPoint bottomRight = ct.transform( bottomRightLonLat );
double xspan = ( bottomRight.x() - topLeft.x() );
QgsWmsBoundingBoxProperty bbox;
bbox.crs = mSettings.mCrsId;
bbox.box = QgsRectangle( topLeft.x(), bottomRight.y(), bottomRight.x(), topLeft.y() );
QgsWmtsTileLayer tl;
tl.tileMode = XYZ;
tl.identifier = "xyz"; // as set in parseUri
tl.boundingBoxes << bbox;
mCaps.mTileLayersSupported.append( tl );
QgsWmtsTileMatrixSet tms;
tms.identifier = "tms0"; // as set in parseUri
tms.crs = mSettings.mCrsId;
mCaps.mTileMatrixSets[tms.identifier] = tms;
int minZoom = 0;
int maxZoom = 18;
if ( parsedUri.hasParam( "zmin" ) )
minZoom = parsedUri.param( "zmin" ).toInt();
if ( parsedUri.hasParam( "zmax" ) )
maxZoom = parsedUri.param( "zmax" ).toInt();
// zoom 0 is one tile for the whole world
for ( int zoom = minZoom; zoom <= maxZoom; ++zoom )
{
QgsWmtsTileMatrix tm;
tm.identifier = QString::number( zoom );
tm.topLeft = topLeft;
tm.tileWidth = 256;
tm.tileHeight = 256;
tm.matrixWidth = pow( 2, zoom );
tm.matrixHeight = pow( 2, zoom );
tm.tres = xspan / ( tm.tileWidth * tm.matrixWidth );
mCaps.mTileMatrixSets[tms.identifier].tileMatrices[tm.tres] = tm;
}
}
Qgis::DataType QgsWmsProvider::dataType( int bandNo ) const
{
return sourceDataType( bandNo );
@ -1835,6 +1924,10 @@ QString QgsWmsProvider::metadata()
{
metadata += tr( "WMS-C" );
}
else if ( l.tileMode == XYZ )
{
metadata += tr( "XYZ" );
}
else
{
metadata += tr( "Invalid tile mode" );

View File

@ -363,6 +363,9 @@ class QgsWmsProvider : public QgsRasterDataProvider
private:
//! In case of XYZ tile layer, setup capabilities from its URI
void setupXyzCapabilities( const QString& uri );
QImage *draw( QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, QgsRasterBlockFeedback* feedback );
/**