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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsrasterlayerrenderer.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : December 2013
       5                 :            :   Copyright            : (C) 2013 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 "qgsrasterlayerrenderer.h"
      17                 :            : 
      18                 :            : #include "qgsmessagelog.h"
      19                 :            : #include "qgsrasterdataprovider.h"
      20                 :            : #include "qgsrasterdrawer.h"
      21                 :            : #include "qgsrasteriterator.h"
      22                 :            : #include "qgsrasterlayer.h"
      23                 :            : #include "qgsrasterprojector.h"
      24                 :            : #include "qgsrendercontext.h"
      25                 :            : #include "qgsproject.h"
      26                 :            : #include "qgsexception.h"
      27                 :            : #include "qgsrasterlayertemporalproperties.h"
      28                 :            : #include "qgsmapclippingutils.h"
      29                 :            : 
      30                 :            : #include <QElapsedTimer>
      31                 :            : #include <QPointer>
      32                 :            : 
      33                 :            : ///@cond PRIVATE
      34                 :            : 
      35                 :          0 : QgsRasterLayerRendererFeedback::QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r )
      36                 :          0 :   : mR( r )
      37                 :          0 :   , mMinimalPreviewInterval( 250 )
      38                 :          0 : {
      39                 :          0 :   setRenderPartialOutput( r->renderContext()->testFlag( QgsRenderContext::RenderPartialOutput ) );
      40                 :          0 : }
      41                 :            : 
      42                 :          0 : void QgsRasterLayerRendererFeedback::onNewData()
      43                 :            : {
      44                 :          0 :   if ( !renderPartialOutput() )
      45                 :          0 :     return;  // we were not asked for partial renders and we may not have a temporary image for overwriting...
      46                 :            : 
      47                 :            :   // update only once upon a time
      48                 :            :   // (preview itself takes some time)
      49                 :          0 :   if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
      50                 :          0 :     return;
      51                 :            : 
      52                 :            :   // TODO: update only the area that got new data
      53                 :            : 
      54                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ), 3 );
      55                 :          0 :   QElapsedTimer t;
      56                 :          0 :   t.start();
      57                 :          0 :   QgsRasterBlockFeedback feedback;
      58                 :          0 :   feedback.setPreviewOnly( true );
      59                 :          0 :   feedback.setRenderPartialOutput( true );
      60                 :          0 :   QgsRasterIterator iterator( mR->mPipe->last() );
      61                 :          0 :   QgsRasterDrawer drawer( &iterator );
      62                 :          0 :   drawer.draw( mR->renderContext()->painter(), mR->mRasterViewPort, &mR->renderContext()->mapToPixel(), &feedback );
      63                 :          0 :   mR->mReadyToCompose = true;
      64                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ), 3 );
      65                 :          0 :   mLastPreview = QTime::currentTime();
      66                 :          0 : }
      67                 :            : 
      68                 :            : ///@endcond
      69                 :            : ///
      70                 :          0 : QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRenderContext &rendererContext )
      71                 :          0 :   : QgsMapLayerRenderer( layer->id(), &rendererContext )
      72                 :          0 :   , mProviderCapabilities( static_cast<QgsRasterDataProvider::Capability>( layer->dataProvider()->capabilities() ) )
      73                 :          0 :   , mFeedback( new QgsRasterLayerRendererFeedback( this ) )
      74                 :          0 : {
      75                 :          0 :   mReadyToCompose = false;
      76                 :          0 :   QgsMapToPixel mapToPixel = rendererContext.mapToPixel();
      77                 :          0 :   if ( rendererContext.mapToPixel().mapRotation() )
      78                 :            :   {
      79                 :            :     // unset rotation for the sake of local computations.
      80                 :            :     // Rotation will be handled by QPainter later
      81                 :            :     // TODO: provide a method of QgsMapToPixel to fetch map center
      82                 :          0 :     //       in geographical units
      83                 :          0 :     QgsPointXY center = mapToPixel.toMapCoordinates(
      84                 :          0 :                           static_cast<int>( mapToPixel.mapWidth() / 2.0 ),
      85                 :          0 :                           static_cast<int>( mapToPixel.mapHeight() / 2.0 )
      86                 :            :                         );
      87                 :          0 :     mapToPixel.setMapRotation( 0, center.x(), center.y() );
      88                 :          0 :   }
      89                 :            : 
      90                 :          0 :   QgsRectangle myProjectedViewExtent;
      91                 :          0 :   QgsRectangle myProjectedLayerExtent;
      92                 :            : 
      93                 :          0 :   if ( rendererContext.coordinateTransform().isValid() )
      94                 :            :   {
      95                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "coordinateTransform set -> project extents." ), 4 );
      96                 :          0 :     if ( rendererContext.extent().xMinimum() == std::numeric_limits<double>::lowest() &&
      97                 :          0 :          rendererContext.extent().yMinimum() == std::numeric_limits<double>::lowest() &&
      98                 :          0 :          rendererContext.extent().xMaximum() == std::numeric_limits<double>::max() &&
      99                 :          0 :          rendererContext.extent().yMaximum() == std::numeric_limits<double>::max() )
     100                 :            :     {
     101                 :            :       // We get in this situation if the view CRS is geographical and the
     102                 :            :       // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
     103                 :            :       // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
     104                 :            :       // Don't try to reproject it now to view extent as this would return
     105                 :            :       // a null rectangle.
     106                 :          0 :       myProjectedViewExtent = rendererContext.extent();
     107                 :          0 :     }
     108                 :            :     else
     109                 :            :     {
     110                 :            :       try
     111                 :            :       {
     112                 :          0 :         QgsCoordinateTransform ct = rendererContext.coordinateTransform();
     113                 :          0 :         ct.setBallparkTransformsAreAppropriate( true );
     114                 :          0 :         myProjectedViewExtent = ct.transformBoundingBox( rendererContext.extent() );
     115                 :          0 :       }
     116                 :            :       catch ( QgsCsException &cs )
     117                 :            :       {
     118                 :          0 :         QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
     119                 :          0 :         myProjectedViewExtent.setMinimal();
     120                 :          0 :       }
     121                 :            :     }
     122                 :            : 
     123                 :            :     try
     124                 :            :     {
     125                 :          0 :       QgsCoordinateTransform ct = rendererContext.coordinateTransform();
     126                 :          0 :       ct.setBallparkTransformsAreAppropriate( true );
     127                 :          0 :       myProjectedLayerExtent = ct.transformBoundingBox( layer->extent() );
     128                 :          0 :     }
     129                 :            :     catch ( QgsCsException &cs )
     130                 :            :     {
     131                 :          0 :       QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
     132                 :          0 :       myProjectedLayerExtent.setMinimal();
     133                 :          0 :     }
     134                 :          0 :   }
     135                 :            :   else
     136                 :            :   {
     137                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "coordinateTransform not set" ), 4 );
     138                 :          0 :     myProjectedViewExtent = rendererContext.extent();
     139                 :          0 :     myProjectedLayerExtent = layer->extent();
     140                 :            :   }
     141                 :            : 
     142                 :            :   // clip raster extent to view extent
     143                 :          0 :   QgsRectangle myRasterExtent = layer->ignoreExtents() ? myProjectedViewExtent : myProjectedViewExtent.intersect( myProjectedLayerExtent );
     144                 :          0 :   if ( myRasterExtent.isEmpty() )
     145                 :            :   {
     146                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "draw request outside view extent." ), 2 );
     147                 :            :     // nothing to do
     148                 :          0 :     return;
     149                 :            :   }
     150                 :            : 
     151                 :          0 :   QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
     152                 :          0 :   QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
     153                 :          0 :   QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
     154                 :          0 :   QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
     155                 :            : 
     156                 :            :   //
     157                 :            :   // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
     158                 :            :   // relating to the size (in pixels and coordinate system units) of the raster part that is
     159                 :            :   // in view in the map window. It also stores the origin.
     160                 :            :   //
     161                 :            :   //this is not a class level member because every time the user pans or zooms
     162                 :            :   //the contents of the rasterViewPort will change
     163                 :          0 :   mRasterViewPort = new QgsRasterViewPort();
     164                 :            : 
     165                 :          0 :   mRasterViewPort->mDrawnExtent = myRasterExtent;
     166                 :          0 :   if ( rendererContext.coordinateTransform().isValid() )
     167                 :            :   {
     168                 :          0 :     mRasterViewPort->mSrcCRS = layer->crs();
     169                 :          0 :     mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
     170                 :          0 :     mRasterViewPort->mTransformContext = rendererContext.transformContext();
     171                 :          0 :   }
     172                 :            :   else
     173                 :            :   {
     174                 :          0 :     mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
     175                 :          0 :     mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
     176                 :            :   }
     177                 :            : 
     178                 :            :   // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
     179                 :          0 :   mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
     180                 :          0 :   mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
     181                 :            : 
     182                 :            :   // align to output device grid, i.e. std::floor/ceil to integers
     183                 :            :   // TODO: this should only be done if paint device is raster - screen, image
     184                 :            :   // for other devices (pdf) it can have floating point origin
     185                 :            :   // we could use floating point for raster devices as well, but respecting the
     186                 :            :   // output device grid should make it more effective as the resampling is done in
     187                 :            :   // the provider anyway
     188                 :          0 :   mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) );
     189                 :          0 :   mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) );
     190                 :          0 :   mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) );
     191                 :          0 :   mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) );
     192                 :            :   // recalc myRasterExtent to aligned values
     193                 :          0 :   myRasterExtent.set(
     194                 :          0 :     mapToPixel.toMapCoordinates( mRasterViewPort->mTopLeftPoint.x(),
     195                 :          0 :                                  mRasterViewPort->mBottomRightPoint.y() ),
     196                 :          0 :     mapToPixel.toMapCoordinates( mRasterViewPort->mBottomRightPoint.x(),
     197                 :          0 :                                  mRasterViewPort->mTopLeftPoint.y() )
     198                 :            :   );
     199                 :            : 
     200                 :            :   //raster viewport top left / bottom right are already rounded to int
     201                 :          0 :   mRasterViewPort->mWidth = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ) );
     202                 :          0 :   mRasterViewPort->mHeight = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ) );
     203                 :            : 
     204                 :            :   //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
     205                 :            :   //mapToPixel.mapUnitsPerPixel() is less then 1,
     206                 :            :   //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
     207                 :            : 
     208                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
     209                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( layer->width() ), 3 );
     210                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( layer->height() ), 3 );
     211                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
     212                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
     213                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
     214                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
     215                 :            : 
     216                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
     217                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
     218                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
     219                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
     220                 :            : 
     221                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
     222                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
     223                 :            : 
     224                 :            :   // /\/\/\ - added to handle zoomed-in rasters
     225                 :            : 
     226                 :            :   // TODO R->mLastViewPort = *mRasterViewPort;
     227                 :            : 
     228                 :            :   // TODO: is it necessary? Probably WMS only?
     229                 :          0 :   layer->dataProvider()->setDpi( 25.4 * rendererContext.scaleFactor() );
     230                 :            : 
     231                 :            : 
     232                 :            :   // copy the whole raster pipe!
     233                 :          0 :   mPipe = new QgsRasterPipe( *layer->pipe() );
     234                 :          0 :   QObject::connect( mPipe->provider(), &QgsRasterDataProvider::statusChanged, layer, &QgsRasterLayer::statusChanged );
     235                 :          0 :   QgsRasterRenderer *rasterRenderer = mPipe->renderer();
     236                 :          0 :   if ( rasterRenderer
     237                 :          0 :        && !( rendererContext.flags() & QgsRenderContext::RenderPreviewJob )
     238                 :          0 :        && !( rendererContext.flags() & QgsRenderContext::Render3DMap ) )
     239                 :            :   {
     240                 :          0 :     layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
     241                 :          0 :   }
     242                 :            : 
     243                 :          0 :   const QgsRasterLayerTemporalProperties *temporalProperties = qobject_cast< const QgsRasterLayerTemporalProperties * >( layer->temporalProperties() );
     244                 :          0 :   if ( temporalProperties->isActive() && renderContext()->isTemporal() )
     245                 :            :   {
     246                 :          0 :     switch ( temporalProperties->mode() )
     247                 :            :     {
     248                 :            :       case QgsRasterLayerTemporalProperties::ModeFixedTemporalRange:
     249                 :          0 :         break;
     250                 :            : 
     251                 :            :       case QgsRasterLayerTemporalProperties::ModeTemporalRangeFromDataProvider:
     252                 :            :         // in this mode we need to pass on the desired render temporal range to the data provider
     253                 :          0 :         if ( mPipe->provider()->temporalCapabilities() )
     254                 :            :         {
     255                 :          0 :           mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( rendererContext.temporalRange() );
     256                 :          0 :           mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
     257                 :          0 :         }
     258                 :          0 :         break;
     259                 :            :     }
     260                 :          0 :   }
     261                 :          0 :   else if ( mPipe->provider()->temporalCapabilities() )
     262                 :            :   {
     263                 :          0 :     mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( QgsDateTimeRange() );
     264                 :          0 :     mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
     265                 :          0 :   }
     266                 :            : 
     267                 :          0 :   mClippingRegions = QgsMapClippingUtils::collectClippingRegionsForLayer( *renderContext(), layer );
     268                 :          0 : }
     269                 :            : 
     270                 :          0 : QgsRasterLayerRenderer::~QgsRasterLayerRenderer()
     271                 :          0 : {
     272                 :          0 :   delete mFeedback;
     273                 :            : 
     274                 :          0 :   delete mRasterViewPort;
     275                 :          0 :   delete mPipe;
     276                 :          0 : }
     277                 :            : 
     278                 :          0 : bool QgsRasterLayerRenderer::render()
     279                 :            : {
     280                 :            :   // Skip rendering of out of view tiles (xyz)
     281                 :          0 :   if ( !mRasterViewPort || ( renderContext()->testFlag( QgsRenderContext::Flag::RenderPreviewJob ) &&
     282                 :          0 :                              !( mProviderCapabilities &
     283                 :            :                                 QgsRasterInterface::Capability::Prefetch ) ) )
     284                 :          0 :     return true;
     285                 :            : 
     286                 :          0 :   QElapsedTimer time;
     287                 :          0 :   time.start();
     288                 :            :   //
     289                 :            :   //
     290                 :            :   // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
     291                 :            :   // so that we can maximise performance of the rendering process. So now we check which drawing
     292                 :            :   // procedure to use :
     293                 :            :   //
     294                 :            : 
     295                 :          0 :   QgsScopedQPainterState painterSate( renderContext()->painter() );
     296                 :          0 :   if ( !mClippingRegions.empty() )
     297                 :            :   {
     298                 :          0 :     bool needsPainterClipPath = false;
     299                 :          0 :     const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), QgsMapLayerType::RasterLayer, needsPainterClipPath );
     300                 :          0 :     if ( needsPainterClipPath )
     301                 :          0 :       renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
     302                 :          0 :   }
     303                 :            : 
     304                 :          0 :   QgsRasterProjector *projector = mPipe->projector();
     305                 :          0 :   bool restoreOldResamplingStage = false;
     306                 :          0 :   QgsRasterPipe::ResamplingStage oldResamplingState = mPipe->resamplingStage();
     307                 :            : 
     308                 :            :   // TODO add a method to interface to get provider and get provider
     309                 :            :   // params in QgsRasterProjector
     310                 :          0 :   if ( projector )
     311                 :            :   {
     312                 :            :     // Force provider resampling if reprojection is needed
     313                 :          0 :     if ( ( mPipe->provider()->providerCapabilities() & QgsRasterDataProvider::ProviderHintCanPerformProviderResampling ) &&
     314                 :          0 :          mRasterViewPort->mSrcCRS != mRasterViewPort->mDestCRS &&
     315                 :          0 :          oldResamplingState != QgsRasterPipe::ResamplingStage::Provider )
     316                 :            :     {
     317                 :          0 :       restoreOldResamplingStage = true;
     318                 :          0 :       mPipe->setResamplingStage( QgsRasterPipe::ResamplingStage::Provider );
     319                 :          0 :     }
     320                 :          0 :     projector->setCrs( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS, mRasterViewPort->mTransformContext );
     321                 :          0 :   }
     322                 :            : 
     323                 :            :   // Drawer to pipe?
     324                 :          0 :   QgsRasterIterator iterator( mPipe->last() );
     325                 :          0 :   QgsRasterDrawer drawer( &iterator );
     326                 :          0 :   drawer.draw( renderContext()->painter(), mRasterViewPort, &renderContext()->mapToPixel(), mFeedback );
     327                 :            : 
     328                 :          0 :   if ( restoreOldResamplingStage )
     329                 :            :   {
     330                 :          0 :     mPipe->setResamplingStage( oldResamplingState );
     331                 :          0 :   }
     332                 :            : 
     333                 :          0 :   const QStringList errors = mFeedback->errors();
     334                 :          0 :   for ( const QString &error : errors )
     335                 :            :   {
     336                 :          0 :     mErrors.append( error );
     337                 :            :   }
     338                 :            : 
     339                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms):     %1" ).arg( time.elapsed(), 5 ), 4 );
     340                 :          0 :   mReadyToCompose = true;
     341                 :            : 
     342                 :          0 :   return !mFeedback->isCanceled();
     343                 :          0 : }
     344                 :            : 
     345                 :          0 : QgsFeedback *QgsRasterLayerRenderer::feedback() const
     346                 :            : {
     347                 :          0 :   return mFeedback;
     348                 :            : }
     349                 :            : 
     350                 :          0 : bool QgsRasterLayerRenderer::forceRasterRender() const
     351                 :            : {
     352                 :            :   // preview of intermediate raster rendering results requires a temporary output image
     353                 :          0 :   return renderContext()->testFlag( QgsRenderContext::RenderPartialOutput );
     354                 :            : }
     355                 :            : 

Generated by: LCOV version 1.14