LCOV - code coverage report
Current view: top level - core/vectortile - qgsvectortilelayerrenderer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 147 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvectortilelayerrenderer.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : March 2020
       5                 :            :   Copyright            : (C) 2020 by Martin Dobias
       6                 :            :   Email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : #include "qgsvectortilelayerrenderer.h"
      17                 :            : 
      18                 :            : #include <QElapsedTimer>
      19                 :            : 
      20                 :            : #include "qgsexpressioncontextutils.h"
      21                 :            : #include "qgsfeedback.h"
      22                 :            : #include "qgslogger.h"
      23                 :            : 
      24                 :            : #include "qgsvectortilemvtdecoder.h"
      25                 :            : #include "qgsvectortilelayer.h"
      26                 :            : #include "qgsvectortileloader.h"
      27                 :            : #include "qgsvectortileutils.h"
      28                 :            : 
      29                 :            : #include "qgslabelingengine.h"
      30                 :            : #include "qgsvectortilelabeling.h"
      31                 :            : #include "qgsmapclippingutils.h"
      32                 :            : 
      33                 :          0 : QgsVectorTileLayerRenderer::QgsVectorTileLayerRenderer( QgsVectorTileLayer *layer, QgsRenderContext &context )
      34                 :          0 :   : QgsMapLayerRenderer( layer->id(), &context )
      35                 :          0 :   , mSourceType( layer->sourceType() )
      36                 :          0 :   , mSourcePath( layer->sourcePath() )
      37                 :          0 :   , mSourceMinZoom( layer->sourceMinZoom() )
      38                 :          0 :   , mSourceMaxZoom( layer->sourceMaxZoom() )
      39                 :          0 :   , mRenderer( layer->renderer()->clone() )
      40                 :          0 :   , mDrawTileBoundaries( layer->isTileBorderRenderingEnabled() )
      41                 :          0 :   , mFeedback( new QgsFeedback )
      42                 :          0 :   , mLayerOpacity( layer->opacity() )
      43                 :          0 : {
      44                 :            : 
      45                 :          0 :   QgsDataSourceUri dsUri;
      46                 :          0 :   dsUri.setEncodedUri( layer->source() );
      47                 :          0 :   mAuthCfg = dsUri.authConfigId();
      48                 :          0 :   mReferer = dsUri.param( QStringLiteral( "referer" ) );
      49                 :            : 
      50                 :          0 :   if ( QgsLabelingEngine *engine = context.labelingEngine() )
      51                 :            :   {
      52                 :          0 :     if ( layer->labeling() )
      53                 :            :     {
      54                 :          0 :       mLabelProvider = layer->labeling()->provider( layer );
      55                 :          0 :       if ( mLabelProvider )
      56                 :            :       {
      57                 :          0 :         engine->addProvider( mLabelProvider );
      58                 :          0 :       }
      59                 :          0 :     }
      60                 :          0 :   }
      61                 :            : 
      62                 :          0 :   mClippingRegions = QgsMapClippingUtils::collectClippingRegionsForLayer( *renderContext(), layer );
      63                 :          0 : }
      64                 :            : 
      65                 :          0 : bool QgsVectorTileLayerRenderer::render()
      66                 :            : {
      67                 :          0 :   QgsRenderContext &ctx = *renderContext();
      68                 :            : 
      69                 :          0 :   if ( ctx.renderingStopped() )
      70                 :          0 :     return false;
      71                 :            : 
      72                 :          0 :   QgsScopedQPainterState painterState( ctx.painter() );
      73                 :            : 
      74                 :          0 :   if ( !mClippingRegions.empty() )
      75                 :          0 :   {
      76                 :          0 :     bool needsPainterClipPath = false;
      77                 :          0 :     const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), QgsMapLayerType::VectorTileLayer, needsPainterClipPath );
      78                 :          0 :     if ( needsPainterClipPath )
      79                 :          0 :       renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
      80                 :          0 :   }
      81                 :            : 
      82                 :          0 :   QElapsedTimer tTotal;
      83                 :          0 :   tTotal.start();
      84                 :            : 
      85                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ), 2 );
      86                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( ctx.rendererScale() ), 2 );
      87                 :            : 
      88                 :          0 :   mTileZoom = QgsVectorTileUtils::scaleToZoomLevel( ctx.rendererScale(), mSourceMinZoom, mSourceMaxZoom );
      89                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ), 2 );
      90                 :            : 
      91                 :          0 :   mTileMatrix = QgsTileMatrix::fromWebMercator( mTileZoom );
      92                 :            : 
      93                 :          0 :   mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() );
      94                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Vector tiles range X: %1 - %2  Y: %3 - %4" )
      95                 :            :                     .arg( mTileRange.startColumn() ).arg( mTileRange.endColumn() )
      96                 :            :                     .arg( mTileRange.startRow() ).arg( mTileRange.endRow() ), 2 );
      97                 :          0 : 
      98                 :            :   // view center is used to sort the order of tiles for fetching and rendering
      99                 :          0 :   QPointF viewCenter = mTileMatrix.mapToTileCoordinates( ctx.extent().center() );
     100                 :            : 
     101                 :          0 :   if ( !mTileRange.isValid() )
     102                 :            :   {
     103                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "Vector tiles - outside of range" ), 2 );
     104                 :          0 :     return true;   // nothing to do
     105                 :            :   }
     106                 :            : 
     107                 :          0 :   bool isAsync = ( mSourceType == QLatin1String( "xyz" ) );
     108                 :            : 
     109                 :          0 :   std::unique_ptr<QgsVectorTileLoader> asyncLoader;
     110                 :          0 :   QList<QgsVectorTileRawData> rawTiles;
     111                 :          0 :   if ( !isAsync )
     112                 :            :   {
     113                 :          0 :     QElapsedTimer tFetch;
     114                 :          0 :     tFetch.start();
     115                 :          0 :     rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange, mAuthCfg, mReferer );
     116                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ), 2 );
     117                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ), 2 );
     118                 :          0 :   }
     119                 :            :   else
     120                 :            :   {
     121                 :          0 :     asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mAuthCfg, mReferer, mFeedback.get() ) );
     122                 :          0 :     QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, asyncLoader.get(), [this]( const QgsVectorTileRawData & rawTile )
     123                 :            :     {
     124                 :          0 :       QgsDebugMsgLevel( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString(), 2 );
     125                 :          0 :       if ( !rawTile.data.isEmpty() )
     126                 :          0 :         decodeAndDrawTile( rawTile );
     127                 :          0 :     } );
     128                 :            :   }
     129                 :            : 
     130                 :          0 :   if ( ctx.renderingStopped() )
     131                 :          0 :     return false;
     132                 :            : 
     133                 :            :   // add @zoom_level variable which can be used in styling
     134                 :          0 :   QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Tiles" ) ); // will be deleted by popper
     135                 :          0 :   scope->setVariable( QStringLiteral( "zoom_level" ), mTileZoom, true );
     136                 :          0 :   scope->setVariable( QStringLiteral( "vector_tile_zoom" ), QgsVectorTileUtils::scaleToZoom( ctx.rendererScale() ), true );
     137                 :          0 :   QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope );
     138                 :            : 
     139                 :          0 :   mRenderer->startRender( *renderContext(), mTileZoom, mTileRange );
     140                 :            : 
     141                 :          0 :   QMap<QString, QSet<QString> > requiredFields = mRenderer->usedAttributes( ctx );
     142                 :            : 
     143                 :          0 :   if ( mLabelProvider )
     144                 :            :   {
     145                 :          0 :     const QMap<QString, QSet<QString> > requiredFieldsLabeling = mLabelProvider->usedAttributes( ctx, mTileZoom );
     146                 :          0 :     for ( auto it = requiredFieldsLabeling.begin(); it != requiredFieldsLabeling.end(); ++it )
     147                 :            :     {
     148                 :          0 :       requiredFields[it.key()].unite( it.value() );
     149                 :          0 :     }
     150                 :          0 :   }
     151                 :            : 
     152                 :          0 :   for ( auto it = requiredFields.constBegin(); it != requiredFields.constEnd(); ++it )
     153                 :          0 :     mPerLayerFields[it.key()] = QgsVectorTileUtils::makeQgisFields( it.value() );
     154                 :            : 
     155                 :          0 :   mRequiredLayers = mRenderer->requiredLayers( ctx, mTileZoom );
     156                 :            : 
     157                 :          0 :   if ( mLabelProvider )
     158                 :            :   {
     159                 :          0 :     mLabelProvider->setFields( mPerLayerFields );
     160                 :          0 :     QSet<QString> attributeNames;  // we don't need this - already got referenced columns in provider constructor
     161                 :          0 :     if ( !mLabelProvider->prepare( ctx, attributeNames ) )
     162                 :            :     {
     163                 :          0 :       ctx.labelingEngine()->removeProvider( mLabelProvider );
     164                 :          0 :       mLabelProvider = nullptr; // provider is deleted by the engine
     165                 :          0 :     }
     166                 :            : 
     167                 :          0 :     mRequiredLayers.unite( mLabelProvider->requiredLayers( ctx, mTileZoom ) );
     168                 :          0 :   }
     169                 :            : 
     170                 :          0 :   if ( !isAsync )
     171                 :            :   {
     172                 :          0 :     for ( QgsVectorTileRawData &rawTile : rawTiles )
     173                 :            :     {
     174                 :          0 :       if ( ctx.renderingStopped() )
     175                 :          0 :         break;
     176                 :            : 
     177                 :          0 :       decodeAndDrawTile( rawTile );
     178                 :            :     }
     179                 :          0 :   }
     180                 :            :   else
     181                 :            :   {
     182                 :            :     // Block until tiles are fetched and rendered. If the rendering gets canceled at some point,
     183                 :            :     // the async loader will catch the signal, abort requests and return from downloadBlocking()
     184                 :          0 :     asyncLoader->downloadBlocking();
     185                 :            :   }
     186                 :            : 
     187                 :          0 :   mRenderer->stopRender( ctx );
     188                 :            : 
     189                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Total time for decoding: %1" ).arg( mTotalDecodeTime / 1000. ), 2 );
     190                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Drawing time: %1" ).arg( mTotalDrawTime / 1000. ), 2 );
     191                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Total time: %1" ).arg( tTotal.elapsed() / 1000. ), 2 );
     192                 :            : 
     193                 :          0 :   return !ctx.renderingStopped();
     194                 :          0 : }
     195                 :            : 
     196                 :          0 : bool QgsVectorTileLayerRenderer::forceRasterRender() const
     197                 :            : {
     198                 :          0 :   return renderContext()->testFlag( QgsRenderContext::UseAdvancedEffects ) && ( !qgsDoubleNear( mLayerOpacity, 1.0 ) );
     199                 :            : }
     200                 :            : 
     201                 :          0 : void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &rawTile )
     202                 :            : {
     203                 :          0 :   QgsRenderContext &ctx = *renderContext();
     204                 :            : 
     205                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Drawing tile " ) + rawTile.id.toString(), 2 );
     206                 :            : 
     207                 :          0 :   QElapsedTimer tLoad;
     208                 :          0 :   tLoad.start();
     209                 :            : 
     210                 :            :   // currently only MVT encoding supported
     211                 :          0 :   QgsVectorTileMVTDecoder decoder;
     212                 :          0 :   if ( !decoder.decode( rawTile.id, rawTile.data ) )
     213                 :            :   {
     214                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString(), 2 );
     215                 :          0 :     return;
     216                 :            :   }
     217                 :            : 
     218                 :          0 :   if ( ctx.renderingStopped() )
     219                 :          0 :     return;
     220                 :            : 
     221                 :          0 :   QgsCoordinateTransform ct = ctx.coordinateTransform();
     222                 :            : 
     223                 :          0 :   QgsVectorTileRendererData tile( rawTile.id );
     224                 :          0 :   tile.setFields( mPerLayerFields );
     225                 :          0 :   tile.setFeatures( decoder.layerFeatures( mPerLayerFields, ct, &mRequiredLayers ) );
     226                 :            : 
     227                 :            :   try
     228                 :            :   {
     229                 :          0 :     tile.setTilePolygon( QgsVectorTileUtils::tilePolygon( rawTile.id, ct, mTileMatrix, ctx.mapToPixel() ) );
     230                 :          0 :   }
     231                 :            :   catch ( QgsCsException & )
     232                 :            :   {
     233                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "Failed to generate tile polygon " ) + rawTile.id.toString(), 2 );
     234                 :            :     return;
     235                 :          0 :   }
     236                 :            : 
     237                 :          0 :   mTotalDecodeTime += tLoad.elapsed();
     238                 :            : 
     239                 :            :   // calculate tile polygon in screen coordinates
     240                 :            : 
     241                 :          0 :   if ( ctx.renderingStopped() )
     242                 :          0 :     return;
     243                 :            : 
     244                 :            :   // set up clipping so that rendering does not go behind tile's extent
     245                 :          0 :   QgsScopedQPainterState savePainterState( ctx.painter() );
     246                 :            :   // we have to intersect with any existing painter clip regions, or we risk overwriting valid clip
     247                 :            :   // regions setup outside of the vector tile renderer (e.g. layout map clip region)
     248                 :          0 :   ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ), Qt::IntersectClip );
     249                 :            : 
     250                 :          0 :   QElapsedTimer tDraw;
     251                 :          0 :   tDraw.start();
     252                 :            : 
     253                 :          0 :   mRenderer->renderTile( tile, ctx );
     254                 :          0 :   mTotalDrawTime += tDraw.elapsed();
     255                 :            : 
     256                 :          0 :   if ( mLabelProvider )
     257                 :          0 :     mLabelProvider->registerTileFeatures( tile, ctx );
     258                 :            : 
     259                 :          0 :   if ( mDrawTileBoundaries )
     260                 :            :   {
     261                 :          0 :     QgsScopedQPainterState savePainterState( ctx.painter() );
     262                 :          0 :     ctx.painter()->setClipping( false );
     263                 :            : 
     264                 :          0 :     QPen pen( Qt::red );
     265                 :          0 :     pen.setWidth( 3 );
     266                 :          0 :     ctx.painter()->setPen( pen );
     267                 :          0 :     ctx.painter()->drawPolygon( tile.tilePolygon() );
     268                 :          0 :   }
     269                 :          0 : }

Generated by: LCOV version 1.14