Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Vector tile layer - part 1 #35341

Merged
merged 27 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8105ad1
Initial work on vector tile layer support
wonder-sk Mar 25, 2020
da57d93
Added two new forgotten files with QgsTileXYZ, QgsTileMatrx, QgsTileR…
wonder-sk Mar 26, 2020
bbc6af5
Fixes to python bindings + some bits from code review
wonder-sk Mar 26, 2020
617db59
Doxygen, spelling, sip fixes
wonder-sk Mar 26, 2020
f43bd8c
Move the generated code for MVT to a directory in external/
wonder-sk Mar 26, 2020
976c0e0
Do not mix class and struct
wonder-sk Mar 26, 2020
4b362b3
Silence warning, avoid indentation on generated MVT files
wonder-sk Mar 26, 2020
5ec1890
Moved to proper QgsMapLayer subclass + icon + optional tile borders
wonder-sk Mar 27, 2020
b590aae
Added forgotten icon for vector tile layer
wonder-sk Mar 27, 2020
8d24e17
Added zmin/zmax parameters to limit usable zoom levels
wonder-sk Mar 27, 2020
6a107c8
Added initial unit test + test data
wonder-sk Mar 27, 2020
3c33bf7
Added a basic rendering test
wonder-sk Mar 27, 2020
9e80f59
Use random colors, fix sip includes, fix warnings in server
wonder-sk Mar 27, 2020
4744777
Handle on-the-fly reprojection correctly
wonder-sk Mar 27, 2020
804ac87
Get rid of qDebug(), wrap strings in QStringLiteral
wonder-sk Mar 27, 2020
81a7af7
Updates from review (debug msg level 2, qBound, wider lines for test)
wonder-sk Mar 28, 2020
4e7809c
Update src/gui/qgsbrowserdockwidget_p.cpp
wonder-sk Mar 28, 2020
8c90de9
Dummy commit to trigger new azure build (PR reopen does nothing)
wonder-sk Mar 30, 2020
93715f7
Do not use pre-generated files from protoc
wonder-sk Mar 30, 2020
7749e75
Addressed review comments for Even
wonder-sk Mar 30, 2020
2cfd699
Make sure that "protoc" tool is available
wonder-sk Mar 31, 2020
a475af2
Force protobuf package installation while it is not in qgis-dev-deps
wonder-sk Mar 31, 2020
c9f519a
Revert last commit: qgis-dev-deps now includes protobuf (thanks Jürgen!)
wonder-sk Apr 1, 2020
43415ee
Actually try to include protobuf-devel
wonder-sk Apr 1, 2020
7e71b23
Hopefully this fixes the win build
wonder-sk Apr 2, 2020
1c4042b
More windows build fixes
wonder-sk Apr 2, 2020
18bd8ab
Fix the fix on windows
wonder-sk Apr 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions python/core/auto_generated/qgstiles.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Returns tile's row index (Y)
int zoomLevel() const;
%Docstring
Returns tile's zoom level (Z)
%End

QString toString() const;
%Docstring
Returns tile coordinates in a formatted string
%End

};
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgstiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class CORE_EXPORT QgsTileXYZ
//! Returns tile's zoom level (Z)
int zoomLevel() const { return mZoomLevel; }

//! Returns tile coordinates in a formatted string
QString toString() const { return QStringLiteral( "X=%1 Y=%2 Z=%3" ).arg( mColumn ).arg( mRow ).arg( mZoomLevel ); }

private:
int mColumn;
int mRow;
Expand Down
44 changes: 22 additions & 22 deletions src/core/vectortile/qgsvectortilebasicrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ void QgsVectorTileBasicRendererStyle::setSymbol( QgsSymbol *sym )

void QgsVectorTileBasicRendererStyle::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
{
elem.setAttribute( "name", mStyleName );
elem.setAttribute( "layer", mLayerName );
elem.setAttribute( "geometry", mGeometryType );
elem.setAttribute( "enabled", mEnabled ? "1" : "0" );
elem.setAttribute( "expression", mExpression );
elem.setAttribute( "min-zoom", mMinZoomLevel );
elem.setAttribute( "max-zoom", mMaxZoomLevel );
elem.setAttribute( QStringLiteral( "name" ), mStyleName );
elem.setAttribute( QStringLiteral( "layer" ), mLayerName );
elem.setAttribute( QStringLiteral( "geometry" ), mGeometryType );
elem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
elem.setAttribute( QStringLiteral( "expression" ), mExpression );
elem.setAttribute( QStringLiteral( "min-zoom" ), mMinZoomLevel );
elem.setAttribute( QStringLiteral( "max-zoom" ), mMaxZoomLevel );

QDomDocument doc = elem.ownerDocument();
QgsSymbolMap symbols;
Expand All @@ -76,13 +76,13 @@ void QgsVectorTileBasicRendererStyle::writeXml( QDomElement &elem, const QgsRead

void QgsVectorTileBasicRendererStyle::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
{
mStyleName = elem.attribute( "name" );
mLayerName = elem.attribute( "layer" );
mGeometryType = static_cast<QgsWkbTypes::GeometryType>( elem.attribute( "geometry" ).toInt() );
mEnabled = elem.attribute( "enabled" ).toInt();
mExpression = elem.attribute( "expression" );
mMinZoomLevel = elem.attribute( "min-zoom" ).toInt();
mMaxZoomLevel = elem.attribute( "max-zoom" ).toInt();
mStyleName = elem.attribute( QStringLiteral( "name" ) );
mLayerName = elem.attribute( QStringLiteral( "layer" ) );
mGeometryType = static_cast<QgsWkbTypes::GeometryType>( elem.attribute( QStringLiteral( "geometry" ) ).toInt() );
mEnabled = elem.attribute( QStringLiteral( "enabled" ) ).toInt();
mExpression = elem.attribute( QStringLiteral( "expression" ) );
mMinZoomLevel = elem.attribute( QStringLiteral( "min-zoom" ) ).toInt();
mMaxZoomLevel = elem.attribute( QStringLiteral( "max-zoom" ) ).toInt();

mSymbol.reset();
QDomElement symbolsElem = elem.firstChildElement( QStringLiteral( "symbols" ) );
Expand All @@ -105,7 +105,7 @@ QgsVectorTileBasicRenderer::QgsVectorTileBasicRenderer()

QString QgsVectorTileBasicRenderer::type() const
{
return "basic";
return QStringLiteral( "basic" );
}

QgsVectorTileBasicRenderer *QgsVectorTileBasicRenderer::clone() const
Expand Down Expand Up @@ -198,10 +198,10 @@ void QgsVectorTileBasicRenderer::renderTile( const QgsVectorTileRendererData &ti
void QgsVectorTileBasicRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
{
QDomDocument doc = elem.ownerDocument();
QDomElement elemStyles = doc.createElement( "styles" );
QDomElement elemStyles = doc.createElement( QStringLiteral( "styles" ) );
for ( const QgsVectorTileBasicRendererStyle &layerStyle : mStyles )
{
QDomElement elemStyle = doc.createElement( "style" );
QDomElement elemStyle = doc.createElement( QStringLiteral( "style" ) );
layerStyle.writeXml( elemStyle, context );
elemStyles.appendChild( elemStyle );
}
Expand All @@ -212,8 +212,8 @@ void QgsVectorTileBasicRenderer::readXml( const QDomElement &elem, const QgsRead
{
mStyles.clear();

QDomElement elemStyles = elem.firstChildElement( "styles" );
QDomElement elemStyle = elemStyles.firstChildElement( "style" );
QDomElement elemStyles = elem.firstChildElement( QStringLiteral( "styles" ) );
QDomElement elemStyle = elemStyles.firstChildElement( QStringLiteral( "style" ) );
while ( !elemStyle.isNull() )
{
QgsVectorTileBasicRendererStyle layerStyle;
Expand Down Expand Up @@ -274,13 +274,13 @@ QList<QgsVectorTileBasicRendererStyle> QgsVectorTileBasicRenderer::simpleStyle(
markerSymbolLayer->setSize( pointSize );
QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol( QgsSymbolLayerList() << markerSymbolLayer );

QgsVectorTileBasicRendererStyle st1( "polygons", QString(), QgsWkbTypes::PolygonGeometry );
QgsVectorTileBasicRendererStyle st1( QStringLiteral( "Polygons" ), QString(), QgsWkbTypes::PolygonGeometry );
st1.setSymbol( fillSymbol );

QgsVectorTileBasicRendererStyle st2( "lines", QString(), QgsWkbTypes::LineGeometry );
QgsVectorTileBasicRendererStyle st2( QStringLiteral( "Lines" ), QString(), QgsWkbTypes::LineGeometry );
st2.setSymbol( lineSymbol );

QgsVectorTileBasicRendererStyle st3( "points", QString(), QgsWkbTypes::PointGeometry );
QgsVectorTileBasicRendererStyle st3( QStringLiteral( "Points" ), QString(), QgsWkbTypes::PointGeometry );
st3.setSymbol( markerSymbol );

QList<QgsVectorTileBasicRendererStyle> lst;
Expand Down
41 changes: 20 additions & 21 deletions src/core/vectortile/qgsvectortilelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "qgsvectortilelayer.h"

#include "qgslogger.h"
#include "qgsvectortilelayerrenderer.h"
#include "qgsmbtilesreader.h"
#include "qgsvectortilebasicrenderer.h"
Expand All @@ -30,9 +31,9 @@ QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseN
QgsDataSourceUri dsUri;
dsUri.setEncodedUri( uri );

mSourceType = dsUri.param( "type" );
mSourcePath = dsUri.param( "url" );
if ( mSourceType == "xyz" )
mSourceType = dsUri.param( QStringLiteral( "type" ) );
mSourcePath = dsUri.param( QStringLiteral( "url" ) );
if ( mSourceType == QStringLiteral( "xyz" ) )
{
// online tiles
mSourceMinZoom = 0;
Expand All @@ -45,36 +46,36 @@ QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseN

setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
}
else if ( mSourceType == "mbtiles" )
else if ( mSourceType == QStringLiteral( "mbtiles" ) )
{
QgsMBTilesReader reader( mSourcePath );
if ( !reader.open() )
{
qDebug() << "failed to open MBTiles file:" << mSourcePath;
QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + mSourcePath );
return;
}

qDebug() << "name:" << reader.metadataValue( "name" );
QgsDebugMsg( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ) );
bool minZoomOk, maxZoomOk;
int minZoom = reader.metadataValue( "minzoom" ).toInt( &minZoomOk );
int maxZoom = reader.metadataValue( "maxzoom" ).toInt( &maxZoomOk );
int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
if ( minZoomOk )
mSourceMinZoom = minZoom;
if ( maxZoomOk )
mSourceMaxZoom = maxZoom;
qDebug() << "zoom range:" << mSourceMinZoom << mSourceMaxZoom;
QgsDebugMsg( QStringLiteral( "zoom range: %1 - %2" ).arg( mSourceMinZoom ).arg( mSourceMaxZoom ) );

QgsRectangle r = reader.extent();
// TODO: reproject to EPSG:3857
setExtent( r );
}
else
{
// TODO: report error - unknown type
QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
return;
}

setCrs( QgsCoordinateReferenceSystem( "EPSG:3857" ) );
setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
setValid( true );

// set a default renderer
Expand Down Expand Up @@ -107,7 +108,7 @@ bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext
bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
{
QDomElement mapLayerNode = layerNode.toElement();
mapLayerNode.setAttribute( "type", "vector-tile" );
mapLayerNode.setAttribute( QStringLiteral( "type" ), QStringLiteral( "vector-tile" ) );

QString errorMsg;
return writeSymbology( layerNode, doc, errorMsg, context );
Expand All @@ -119,21 +120,19 @@ bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMess

readCommonStyle( elem, context, categories );

QDomElement elemRenderer = elem.firstChildElement( "renderer" );
QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
if ( elemRenderer.isNull() )
{
errorMessage = "Missing <renderer> tag";
errorMessage = tr( "Missing <renderer> tag" );
return false;
}
QString rendererType = elemRenderer.attribute( "type" );
QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
QgsVectorTileRenderer *r = nullptr;
if ( rendererType == "basic" )
if ( rendererType == QStringLiteral( "basic" ) )
r = new QgsVectorTileBasicRenderer;
//else if ( rendererType == "mapbox-gl" )
// r = new MapboxGLStyleRenderer;
else
{
errorMessage = "Unknown renderer type: " + rendererType;
errorMessage = tr( "Unknown renderer type: " ) + rendererType;
return false;
}

Expand All @@ -150,8 +149,8 @@ bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QStr

if ( mRenderer )
{
QDomElement elemRenderer = doc.createElement( "renderer" );
elemRenderer.setAttribute( "type", mRenderer->type() );
QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
mRenderer->writeXml( elemRenderer, context );
elem.appendChild( elemRenderer );
}
Expand Down
32 changes: 17 additions & 15 deletions src/core/vectortile/qgsvectortilelayerrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "qgsexpressioncontextutils.h"
#include "qgsfeedback.h"
#include "qgslogger.h"

#include "qgsvectortilemvtdecoder.h"
#include "qgsvectortilelayer.h"
Expand Down Expand Up @@ -48,26 +49,29 @@ bool QgsVectorTileLayerRenderer::render()
QElapsedTimer tTotal;
tTotal.start();

qDebug() << "MVT rend" << ctx.extent().toString( -1 );
QgsDebugMsg( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ) );
wonder-sk marked this conversation as resolved.
Show resolved Hide resolved
QgsDebugMsg( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( ctx.rendererScale() ) );

mTileZoom = QgsVectorTileUtils::scaleToZoomLevel( ctx.rendererScale(), mSourceMinZoom, mSourceMaxZoom );
qDebug() << "MVT zoom level" << mTileZoom;
QgsDebugMsg( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ) );

mTileMatrix = QgsTileMatrix::fromWebMercator( mTileZoom );

mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() );
qDebug() << "MVT tile range" << mTileRange.startColumn() << mTileRange.endColumn() << " | " << mTileRange.startRow() << mTileRange.endRow();
QgsDebugMsg( QStringLiteral( "Vector tiles range X: %1 - %2 Y: %3 - %4" )
.arg( mTileRange.startColumn() ).arg( mTileRange.endColumn() )
.arg( mTileRange.startRow() ).arg( mTileRange.endRow() ) );

// view center is used to sort the order of tiles for fetching and rendering
QPointF viewCenter = mTileMatrix.mapToTileCoordinates( ctx.extent().center() );

if ( !mTileRange.isValid() )
{
qDebug() << "outside of range";
QgsDebugMsg( QStringLiteral( "Vector tiles - outside of range" ) );
return true; // nothing to do
}

bool isAsync = ( mSourceType == "xyz" );
bool isAsync = ( mSourceType == QStringLiteral( "xyz" ) );

std::unique_ptr<QgsVectorTileLoader> asyncLoader;
QList<QgsVectorTileRawData> rawTiles;
Expand All @@ -76,15 +80,15 @@ bool QgsVectorTileLayerRenderer::render()
QElapsedTimer tFetch;
tFetch.start();
rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileZoom, viewCenter, mTileRange );
qDebug() << "FETCH TIME" << tFetch.elapsed() / 1000.;
qDebug() << "fetched tiles:" << rawTiles.count();
QgsDebugMsg( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ) );
QgsDebugMsg( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ) );
}
else
{
asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileZoom, mTileRange, viewCenter, mFeedback.get() ) );
QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, [this]( const QgsVectorTileRawData & rawTile )
{
qDebug() << "got async tile" << rawTile.id.column() << rawTile.id.row() << rawTile.id.zoomLevel();
QgsDebugMsg( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString() );
if ( !rawTile.data.isEmpty() )
decodeAndDrawTile( rawTile );
} );
Expand Down Expand Up @@ -122,9 +126,9 @@ bool QgsVectorTileLayerRenderer::render()

ctx.painter()->setClipping( false );

qDebug() << "DECODE TIME" << mTotalDecodeTime / 1000.;
qDebug() << "DRAW TIME" << mTotalDrawTime / 1000.;
qDebug() << "TOTAL TIME" << tTotal.elapsed() / 1000.;
QgsDebugMsg( QStringLiteral( "Total time for decoding: %1" ).arg( mTotalDecodeTime / 1000. ) );
QgsDebugMsg( QStringLiteral( "Drawing time: %1" ).arg( mTotalDrawTime / 1000. ) );
QgsDebugMsg( QStringLiteral( "Total time: %1" ).arg( tTotal.elapsed() / 1000. ) );

return !ctx.renderingStopped();
}
Expand All @@ -133,7 +137,7 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &
{
QgsRenderContext &ctx = *renderContext();

qDebug() << "decoding tile " << rawTile.id.zoomLevel() << rawTile.id.column() << rawTile.id.row();
QgsDebugMsgLevel( QStringLiteral( "Drawing tile " ) + rawTile.id.toString(), 2 );

QElapsedTimer tLoad;
tLoad.start();
Expand All @@ -142,7 +146,7 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &
QgsVectorTileMVTDecoder decoder;
if ( !decoder.decode( rawTile.id, rawTile.data ) )
{
qDebug() << "Failed to parse raw tile data!";
QgsDebugMsg( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString() );
return;
}

Expand All @@ -166,8 +170,6 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &

ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ) );

qDebug() << "drawing tile" << tile.id().zoomLevel() << tile.id().column() << tile.id().row();

QElapsedTimer tDraw;
tDraw.start();

Expand Down
Loading