Skip to content

Commit c4181fa

Browse files
committed
[FEATURE] Native support for XYZ tile layers
Yes, that means OpenStreetMap tiles and various other sources! No GUI so far...
1 parent 0c51c3f commit c4181fa

File tree

4 files changed

+161
-21
lines changed

4 files changed

+161
-21
lines changed

src/providers/wms/qgswmscapabilities.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,37 @@ bool QgsWmsSettings::parseUri( const QString& uriString )
4040
QgsDataSourceUri uri;
4141
uri.setEncodedUri( uriString );
4242

43+
mXyz = false; // assume WMS / WMTS
44+
45+
if ( uri.param( "type" ) == "xyz" )
46+
{
47+
// for XYZ tiles most of the things do not apply
48+
mTiled = true;
49+
mXyz = true;
50+
mTileDimensionValues.clear();
51+
mTileMatrixSetId = "tms0";
52+
mMaxWidth = 0;
53+
mMaxHeight = 0;
54+
mHttpUri = uri.param( "url" );
55+
mBaseUrl = mHttpUri;
56+
mAuth.mUserName.clear();
57+
mAuth.mPassword.clear();
58+
mAuth.mReferer.clear();
59+
mAuth.mAuthCfg.clear();
60+
mIgnoreGetMapUrl = false;
61+
mIgnoreGetFeatureInfoUrl = false;
62+
mSmoothPixmapTransform = false;
63+
mDpiMode = dpiNone; // does not matter what we set here
64+
mActiveSubLayers = QStringList( "xyz" ); // just a placeholder to have one sub-layer
65+
mActiveSubStyles = QStringList( "xyz" ); // just a placeholder to have one sub-style
66+
mActiveSubLayerVisibility.clear();
67+
mFeatureCount = 0;
68+
mImageMimeType.clear();
69+
mCrsId = "EPSG:3857";
70+
mEnableContextualLegend = false;
71+
return true;
72+
}
73+
4374
mTiled = false;
4475
mTileDimensionValues.clear();
4576

src/providers/wms/qgswmscapabilities.h

+27-14
Original file line numberDiff line numberDiff line change
@@ -339,18 +339,20 @@ struct QgsWmtsTileMatrix
339339

340340
struct QgsWmtsTileMatrixSet
341341
{
342-
QString identifier;
343-
QString title, abstract;
344-
QStringList keywords;
345-
QString crs;
346-
QString wkScaleSet;
342+
QString identifier; //!< tile matrix set identifier
343+
QString title; //!< human readable tile matrix set name
344+
QString abstract; //!< brief description of the tile matrix set
345+
QStringList keywords; //!< list of words/phrases to describe the dataset
346+
QString crs; //!< CRS of the tile matrix set
347+
QString wkScaleSet; //!< optional reference to a well-known scale set
348+
//! available tile matrixes (key = pixel span in map units)
347349
QMap<double, QgsWmtsTileMatrix> tileMatrices;
348350

349351
//! Returns closest tile resolution to the requested one. (resolution = width [map units] / with [pixels])
350352
const QgsWmtsTileMatrix* findNearestResolution( double vres ) const;
351353
};
352354

353-
enum QgsTileMode { WMTS, WMSC };
355+
enum QgsTileMode { WMTS, WMSC, XYZ };
354356

355357
struct QgsWmtsTileMatrixLimits
356358
{
@@ -382,16 +384,22 @@ struct QgsWmtsStyle
382384
QList<QgsWmtsLegendURL> legendURLs;
383385
};
384386

387+
/**
388+
* In case of multi-dimensional data, the service metadata can describe their multi-
389+
* dimensionality and tiles can be requested at specific values in these dimensions.
390+
* Examples of dimensions are Time, Elevation and Band.
391+
*/
385392
struct QgsWmtsDimension
386393
{
387-
QString identifier;
388-
QString title, abstract;
389-
QStringList keywords;
390-
QString UOM;
391-
QString unitSymbol;
392-
QString defaultValue;
393-
bool current;
394-
QStringList values;
394+
QString identifier; //!< name of the dimensional axis
395+
QString title; //!< human readable name
396+
QString abstract; //!< brief description of the dimension
397+
QStringList keywords; //!< list of words/phrases to describe the dataset
398+
QString UOM; //!< units of measure of dimensional axis
399+
QString unitSymbol; //!< symbol of the units
400+
QString defaultValue; //!< default value to be used if value is not specified in request
401+
bool current; //!< indicates whether temporal data are normally kept current
402+
QStringList values; //!< available values for this dimension
395403
};
396404

397405
struct QgsWmtsTileLayer
@@ -404,6 +412,7 @@ struct QgsWmtsTileLayer
404412
QStringList formats;
405413
QStringList infoFormats;
406414
QString defaultStyle;
415+
//! available dimensions (optional, for multi-dimensional data)
407416
QHash<QString, QgsWmtsDimension> dimensions;
408417
QHash<QString, QgsWmtsStyle> styles;
409418
QHash<QString, QgsWmtsTileMatrixSetLink> setLinks;
@@ -532,7 +541,11 @@ class QgsWmsSettings
532541

533542
//! layer is tiled, tile layer and active matrix set
534543
bool mTiled;
544+
//! whether we actually work with XYZ tiles instead of WMS / WMTS
545+
bool mXyz;
546+
//! chosen values for dimensions in case of multi-dimensional data (key=dim id, value=dim value)
535547
QHash<QString, QString> mTileDimensionValues;
548+
//! name of the chosen tile matrix set
536549
QString mTileMatrixSetId;
537550

538551
/**

src/providers/wms/qgswmsprovider.cpp

+100-7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ static QString DEFAULT_LATLON_CRS = "CRS:84";
7979

8080
QMap<QString, QgsWmsStatistics::Stat> QgsWmsStatistics::sData;
8181

82+
8283
QgsWmsProvider::QgsWmsProvider( QString const& uri, const QgsWmsCapabilities* capabilities )
8384
: QgsRasterDataProvider( uri )
8485
, mHttpGetLegendGraphicResponse( nullptr )
@@ -110,14 +111,26 @@ QgsWmsProvider::QgsWmsProvider( QString const& uri, const QgsWmsCapabilities* ca
110111
if ( !addLayers() )
111112
return;
112113

113-
// if there are already parsed capabilities, use them!
114-
if ( capabilities )
115-
mCaps = *capabilities;
116-
117-
// Make sure we have capabilities - other functions here may need them
118-
if ( !retrieveServerCapabilities() )
114+
if ( mSettings.mXyz )
119115
{
120-
return;
116+
// we are working with XYZ tiles
117+
// no need to get capabilities, the whole definition is in URI
118+
// so we just generate a dummy WMTS definition
119+
setupXyzCapabilities( uri );
120+
}
121+
else
122+
{
123+
// we are working with WMS / WMTS server
124+
125+
// if there are already parsed capabilities, use them!
126+
if ( capabilities )
127+
mCaps = *capabilities;
128+
129+
// Make sure we have capabilities - other functions here may need them
130+
if ( !retrieveServerCapabilities() )
131+
{
132+
return;
133+
}
121134
}
122135

123136
// setImageCrs is using mTiled !!!
@@ -781,6 +794,28 @@ QImage *QgsWmsProvider::draw( QgsRectangle const & viewExtent, int pixelWidth, i
781794
}
782795
break;
783796

797+
case XYZ:
798+
{
799+
QString url = mSettings.mBaseUrl;
800+
int z = tm->identifier.toInt();
801+
int i = 0;
802+
for ( int row = row0; row <= row1; row++ )
803+
{
804+
for ( int col = col0; col <= col1; col++ )
805+
{
806+
QString turl( url );
807+
turl.replace( "{x}", QString::number( col ), Qt::CaseInsensitive );
808+
turl.replace( "{y}", QString::number( row ), Qt::CaseInsensitive );
809+
turl.replace( "{z}", QString::number( z ), Qt::CaseInsensitive );
810+
811+
if ( feedback && !feedback->preview_only )
812+
QgsDebugMsg( QString( "tileRequest %1 %2/%3 (%4,%5): %6" ).arg( mTileReqNo ).arg( i++ ).arg( n ).arg( row ).arg( col ).arg( turl ) );
813+
requests << QgsWmsTiledImageDownloadHandler::TileRequest( turl, tm->tileRect( col, row ), i );
814+
}
815+
}
816+
}
817+
break;
818+
784819
default:
785820
QgsDebugMsg( QString( "unexpected tile mode %1" ).arg( mTileLayer->tileMode ) );
786821
return image;
@@ -935,6 +970,60 @@ bool QgsWmsProvider::retrieveServerCapabilities( bool forceRefresh )
935970
}
936971

937972

973+
void QgsWmsProvider::setupXyzCapabilities( const QString &uri )
974+
{
975+
QgsDataSourceUri parsedUri;
976+
parsedUri.setEncodedUri( uri );
977+
978+
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( "EPSG:4326" ), QgsCoordinateReferenceSystem( mSettings.mCrsId ) );
979+
// the whole world is projected to a square:
980+
// X going from 180 W to 180 E
981+
// Y going from ~85 N to ~85 S (=atan(sinh(pi)) ... to get a square)
982+
QgsPoint topLeftLonLat( -180, 180.0 / M_PI * atan( sinh( M_PI ) ) );
983+
QgsPoint bottomRightLonLat( 180, 180.0 / M_PI * atan( sinh( -M_PI ) ) );
984+
QgsPoint topLeft = ct.transform( topLeftLonLat );
985+
QgsPoint bottomRight = ct.transform( bottomRightLonLat );
986+
double xspan = ( bottomRight.x() - topLeft.x() );
987+
988+
QgsWmsBoundingBoxProperty bbox;
989+
bbox.crs = mSettings.mCrsId;
990+
bbox.box = QgsRectangle( topLeft.x(), bottomRight.y(), bottomRight.x(), topLeft.y() );
991+
992+
QgsWmtsTileLayer tl;
993+
tl.tileMode = XYZ;
994+
tl.identifier = "xyz"; // as set in parseUri
995+
tl.boundingBoxes << bbox;
996+
mCaps.mTileLayersSupported.append( tl );
997+
998+
QgsWmtsTileMatrixSet tms;
999+
tms.identifier = "tms0"; // as set in parseUri
1000+
tms.crs = mSettings.mCrsId;
1001+
mCaps.mTileMatrixSets[tms.identifier] = tms;
1002+
1003+
int minZoom = 0;
1004+
int maxZoom = 18;
1005+
if ( parsedUri.hasParam( "zmin" ) )
1006+
minZoom = parsedUri.param( "zmin" ).toInt();
1007+
if ( parsedUri.hasParam( "zmax" ) )
1008+
maxZoom = parsedUri.param( "zmax" ).toInt();
1009+
1010+
// zoom 0 is one tile for the whole world
1011+
for ( int zoom = minZoom; zoom <= maxZoom; ++zoom )
1012+
{
1013+
QgsWmtsTileMatrix tm;
1014+
tm.identifier = QString::number( zoom );
1015+
tm.topLeft = topLeft;
1016+
tm.tileWidth = 256;
1017+
tm.tileHeight = 256;
1018+
tm.matrixWidth = pow( 2, zoom );
1019+
tm.matrixHeight = pow( 2, zoom );
1020+
tm.tres = xspan / ( tm.tileWidth * tm.matrixWidth );
1021+
1022+
mCaps.mTileMatrixSets[tms.identifier].tileMatrices[tm.tres] = tm;
1023+
}
1024+
}
1025+
1026+
9381027
Qgis::DataType QgsWmsProvider::dataType( int bandNo ) const
9391028
{
9401029
return sourceDataType( bandNo );
@@ -1835,6 +1924,10 @@ QString QgsWmsProvider::metadata()
18351924
{
18361925
metadata += tr( "WMS-C" );
18371926
}
1927+
else if ( l.tileMode == XYZ )
1928+
{
1929+
metadata += tr( "XYZ" );
1930+
}
18381931
else
18391932
{
18401933
metadata += tr( "Invalid tile mode" );

src/providers/wms/qgswmsprovider.h

+3
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ class QgsWmsProvider : public QgsRasterDataProvider
363363

364364
private:
365365

366+
//! In case of XYZ tile layer, setup capabilities from its URI
367+
void setupXyzCapabilities( const QString& uri );
368+
366369
QImage *draw( QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, QgsRasterBlockFeedback* feedback );
367370

368371
/**

0 commit comments

Comments
 (0)