"Fossies" - the Fresh Open Source Software Archive

Member "QGIS-final-3_14_16/src/core/vectortile/qgsvectortilelayerrenderer.cpp" (11 Sep 2020, 8499 Bytes) of package /linux/misc/QGIS-final-3_14_16.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "qgsvectortilelayerrenderer.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3_14_15_vs_3_14_16.

    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 
   32 
   33 QgsVectorTileLayerRenderer::QgsVectorTileLayerRenderer( QgsVectorTileLayer *layer, QgsRenderContext &context )
   34   : QgsMapLayerRenderer( layer->id(), &context )
   35   , mSourceType( layer->sourceType() )
   36   , mSourcePath( layer->sourcePath() )
   37   , mSourceMinZoom( layer->sourceMinZoom() )
   38   , mSourceMaxZoom( layer->sourceMaxZoom() )
   39   , mRenderer( layer->renderer()->clone() )
   40   , mDrawTileBoundaries( layer->isTileBorderRenderingEnabled() )
   41   , mFeedback( new QgsFeedback )
   42 {
   43 
   44   if ( QgsLabelingEngine *engine = context.labelingEngine() )
   45   {
   46     if ( layer->labeling() )
   47     {
   48       mLabelProvider = layer->labeling()->provider( layer );
   49       if ( mLabelProvider )
   50       {
   51         engine->addProvider( mLabelProvider );
   52       }
   53     }
   54   }
   55 
   56 }
   57 
   58 bool QgsVectorTileLayerRenderer::render()
   59 {
   60   QgsRenderContext &ctx = *renderContext();
   61 
   62   if ( ctx.renderingStopped() )
   63     return false;
   64 
   65   ctx.painter()->save();
   66 
   67   QElapsedTimer tTotal;
   68   tTotal.start();
   69 
   70   QgsDebugMsgLevel( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ), 2 );
   71   QgsDebugMsgLevel( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( ctx.rendererScale() ), 2 );
   72 
   73   mTileZoom = QgsVectorTileUtils::scaleToZoomLevel( ctx.rendererScale(), mSourceMinZoom, mSourceMaxZoom );
   74   QgsDebugMsgLevel( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ), 2 );
   75 
   76   mTileMatrix = QgsTileMatrix::fromWebMercator( mTileZoom );
   77 
   78   mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() );
   79   QgsDebugMsgLevel( QStringLiteral( "Vector tiles range X: %1 - %2  Y: %3 - %4" )
   80                     .arg( mTileRange.startColumn() ).arg( mTileRange.endColumn() )
   81                     .arg( mTileRange.startRow() ).arg( mTileRange.endRow() ), 2 );
   82 
   83   // view center is used to sort the order of tiles for fetching and rendering
   84   QPointF viewCenter = mTileMatrix.mapToTileCoordinates( ctx.extent().center() );
   85 
   86   if ( !mTileRange.isValid() )
   87   {
   88     QgsDebugMsgLevel( QStringLiteral( "Vector tiles - outside of range" ), 2 );
   89     ctx.painter()->restore();
   90     return true;   // nothing to do
   91   }
   92 
   93   bool isAsync = ( mSourceType == QStringLiteral( "xyz" ) );
   94 
   95   std::unique_ptr<QgsVectorTileLoader> asyncLoader;
   96   QList<QgsVectorTileRawData> rawTiles;
   97   if ( !isAsync )
   98   {
   99     QElapsedTimer tFetch;
  100     tFetch.start();
  101     rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange );
  102     QgsDebugMsgLevel( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ), 2 );
  103     QgsDebugMsgLevel( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ), 2 );
  104   }
  105   else
  106   {
  107     asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mFeedback.get() ) );
  108     QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, [this]( const QgsVectorTileRawData & rawTile )
  109     {
  110       QgsDebugMsgLevel( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString(), 2 );
  111       if ( !rawTile.data.isEmpty() )
  112         decodeAndDrawTile( rawTile );
  113     } );
  114   }
  115 
  116   if ( ctx.renderingStopped() )
  117   {
  118     ctx.painter()->restore();
  119     return false;
  120   }
  121 
  122   // add @zoom_level variable which can be used in styling
  123   QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Tiles" ) ); // will be deleted by popper
  124   scope->setVariable( "zoom_level", mTileZoom, true );
  125   QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope );
  126 
  127   mRenderer->startRender( *renderContext(), mTileZoom, mTileRange );
  128 
  129   QMap<QString, QSet<QString> > requiredFields = mRenderer->usedAttributes( ctx );
  130 
  131   if ( mLabelProvider )
  132   {
  133     QMap<QString, QSet<QString> > requiredFieldsLabeling = mLabelProvider->usedAttributes( ctx, mTileZoom );
  134     for ( QString layerName : requiredFieldsLabeling.keys() )
  135     {
  136       requiredFields[layerName].unite( requiredFieldsLabeling[layerName] );
  137     }
  138   }
  139 
  140   QMap<QString, QgsFields> perLayerFields;
  141   for ( QString layerName : requiredFields.keys() )
  142     mPerLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( requiredFields[layerName] );
  143 
  144   if ( mLabelProvider )
  145   {
  146     mLabelProvider->setFields( mPerLayerFields );
  147     QSet<QString> attributeNames;  // we don't need this - already got referenced columns in provider constructor
  148     if ( !mLabelProvider->prepare( ctx, attributeNames ) )
  149     {
  150       ctx.labelingEngine()->removeProvider( mLabelProvider );
  151       mLabelProvider = nullptr; // provider is deleted by the engine
  152     }
  153   }
  154 
  155   if ( !isAsync )
  156   {
  157     for ( QgsVectorTileRawData &rawTile : rawTiles )
  158     {
  159       if ( ctx.renderingStopped() )
  160         break;
  161 
  162       decodeAndDrawTile( rawTile );
  163     }
  164   }
  165   else
  166   {
  167     // Block until tiles are fetched and rendered. If the rendering gets canceled at some point,
  168     // the async loader will catch the signal, abort requests and return from downloadBlocking()
  169     asyncLoader->downloadBlocking();
  170   }
  171 
  172   mRenderer->stopRender( ctx );
  173 
  174   QgsDebugMsgLevel( QStringLiteral( "Total time for decoding: %1" ).arg( mTotalDecodeTime / 1000. ), 2 );
  175   QgsDebugMsgLevel( QStringLiteral( "Drawing time: %1" ).arg( mTotalDrawTime / 1000. ), 2 );
  176   QgsDebugMsgLevel( QStringLiteral( "Total time: %1" ).arg( tTotal.elapsed() / 1000. ), 2 );
  177 
  178   ctx.painter()->restore();
  179   return !ctx.renderingStopped();
  180 }
  181 
  182 void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &rawTile )
  183 {
  184   QgsRenderContext &ctx = *renderContext();
  185 
  186   QgsDebugMsgLevel( QStringLiteral( "Drawing tile " ) + rawTile.id.toString(), 2 );
  187 
  188   QElapsedTimer tLoad;
  189   tLoad.start();
  190 
  191   // currently only MVT encoding supported
  192   QgsVectorTileMVTDecoder decoder;
  193   if ( !decoder.decode( rawTile.id, rawTile.data ) )
  194   {
  195     QgsDebugMsgLevel( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString(), 2 );
  196     return;
  197   }
  198 
  199   if ( ctx.renderingStopped() )
  200     return;
  201 
  202   QgsCoordinateTransform ct = ctx.coordinateTransform();
  203 
  204   QgsVectorTileRendererData tile( rawTile.id );
  205   tile.setFields( mPerLayerFields );
  206   tile.setFeatures( decoder.layerFeatures( mPerLayerFields, ct ) );
  207   tile.setTilePolygon( QgsVectorTileUtils::tilePolygon( rawTile.id, ct, mTileMatrix, ctx.mapToPixel() ) );
  208 
  209   mTotalDecodeTime += tLoad.elapsed();
  210 
  211   // calculate tile polygon in screen coordinates
  212 
  213   if ( ctx.renderingStopped() )
  214     return;
  215 
  216   // set up clipping so that rendering does not go behind tile's extent
  217   ctx.painter()->save();
  218   // we have to intersect with any existing painter clip regions, or we risk overwriting valid clip
  219   // regions setup outside of the vector tile renderer (e.g. layout map clip region)
  220   ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ), Qt::IntersectClip );
  221 
  222   QElapsedTimer tDraw;
  223   tDraw.start();
  224 
  225   mRenderer->renderTile( tile, ctx );
  226   mTotalDrawTime += tDraw.elapsed();
  227 
  228   if ( mLabelProvider )
  229     mLabelProvider->registerTileFeatures( tile, ctx );
  230 
  231   if ( mDrawTileBoundaries )
  232   {
  233     ctx.painter()->save();
  234     ctx.painter()->setClipping( false );
  235 
  236     QPen pen( Qt::red );
  237     pen.setWidth( 3 );
  238     ctx.painter()->setPen( pen );
  239     ctx.painter()->drawPolygon( tile.tilePolygon() );
  240     ctx.painter()->restore();
  241   }
  242   ctx.painter()->restore();
  243 }