Add api to allow redrawing of previously collected results for a single

elevation profile source

And use this to just redraw the existing results whenever a setting
relating only to the appearance of the profile chart is changed,
instead of regenerating the whole chart
This commit is contained in:
Nyall Dawson 2022-04-26 11:21:43 +10:00
parent 1ecf9dce5e
commit a35e74c656
14 changed files with 212 additions and 14 deletions

View File

@ -140,6 +140,16 @@ Returns the range of the retrieved elevation values
virtual QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context );
%Docstring
Snaps a ``point`` to the generated elevation profile.
%End
virtual void updateFromGenerator( const QgsAbstractProfileGenerator *generator );
%Docstring
Updates the results to match the properties from the specified ``generator``.
For instance, this method can be used to replace any stored properties relating to rendering
the gathered results to reflect the ``generator``'s current properties.
The base class method does nothing.
%End
};

View File

@ -46,6 +46,11 @@ results.
~QgsProfilePlotRenderer();
QStringList sourceIds() const;
%Docstring
Returns the ordered list of source IDs for the sources used by the renderer.
%End
void startGeneration();
%Docstring
Start the generation job and immediately return.
@ -73,6 +78,13 @@ Block until the current job has finished.
bool isActive() const;
%Docstring
Returns ``True`` if the generation job is currently running in background.
%End
void replaceSource( QgsAbstractProfileSource *source );
%Docstring
Replaces the existing source with matching ID.
The matching stored source will be deleted and replaced with ``source``.
%End
bool invalidateResults( QgsAbstractProfileSource *source );
@ -100,14 +112,18 @@ Does nothing if the generation is already in progress.
Returns the limits of the retrieved elevation values.
%End
QImage renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax );
QImage renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId = QString() );
%Docstring
Renders a portion of the profile to an image with the given ``width`` and ``height``.
If ``sourceId`` is empty then all sources will be rendered, otherwise only the matching source will be rendered.
%End
void render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax );
void render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId = QString() );
%Docstring
Renders a portion of the profile using the specified render ``context``.
If ``sourceId`` is empty then all sources will be rendered, otherwise only the matching source will be rendered.
%End
QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context );

View File

@ -63,3 +63,8 @@ QgsProfileSnapResult QgsAbstractProfileResults::snapPoint( const QgsProfilePoint
{
return QgsProfileSnapResult();
}
void QgsAbstractProfileResults::updateFromGenerator( const QgsAbstractProfileGenerator * )
{
}

View File

@ -116,6 +116,7 @@ class CORE_EXPORT QgsProfileRenderContext
};
class QgsAbstractProfileGenerator;
/**
* \brief Abstract base class for storage of elevation profiles.
@ -164,6 +165,16 @@ class CORE_EXPORT QgsAbstractProfileResults
* Snaps a \a point to the generated elevation profile.
*/
virtual QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context );
/**
* Updates the results to match the properties from the specified \a generator.
*
* For instance, this method can be used to replace any stored properties relating to rendering
* the gathered results to reflect the \a generator's current properties.
*
* The base class method does nothing.
*/
virtual void updateFromGenerator( const QgsAbstractProfileGenerator *generator );
};
/**

View File

@ -46,6 +46,17 @@ QgsProfilePlotRenderer::~QgsProfilePlotRenderer()
}
}
QStringList QgsProfilePlotRenderer::sourceIds() const
{
QStringList res;
res.reserve( mGenerators.size() );
for ( const auto &it : mGenerators )
{
res.append( it->sourceId() );
}
return res;
}
void QgsProfilePlotRenderer::startGeneration()
{
if ( isActive() )
@ -125,7 +136,17 @@ bool QgsProfilePlotRenderer::isActive() const
return mStatus != Idle;
}
void QgsProfilePlotRenderer::replaceSource( QgsAbstractProfileSource *source )
{
replaceSourceInternal( source, false );
}
bool QgsProfilePlotRenderer::invalidateResults( QgsAbstractProfileSource *source )
{
return replaceSourceInternal( source, true );
}
bool QgsProfilePlotRenderer::replaceSourceInternal( QgsAbstractProfileSource *source, bool clearPreviousResults )
{
if ( !source )
return false;
@ -141,9 +162,16 @@ bool QgsProfilePlotRenderer::invalidateResults( QgsAbstractProfileSource *source
if ( job.generator && job.generator->sourceId() == sourceId )
{
res = true;
job.results.reset();
if ( clearPreviousResults )
{
job.results.reset();
job.complete = false;
}
else if ( job.results )
{
job.results->updateFromGenerator( generator.get() );
}
job.generator = generator.get();
job.complete = false;
for ( auto it = mGenerators.begin(); it != mGenerators.end(); )
{
if ( ( *it )->sourceId() == sourceId )
@ -186,7 +214,7 @@ QgsDoubleRange QgsProfilePlotRenderer::zRange() const
return QgsDoubleRange( min, max );
}
QImage QgsProfilePlotRenderer::renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax )
QImage QgsProfilePlotRenderer::renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId )
{
QImage res( width, height, QImage::Format_ARGB32_Premultiplied );
res.fill( Qt::transparent );
@ -196,13 +224,13 @@ QImage QgsProfilePlotRenderer::renderToImage( int width, int height, double dist
QgsRenderContext context = QgsRenderContext::fromQPainter( &p );
context.setFlag( Qgis::RenderContextFlag::Antialiasing, true );
context.setPainterFlagsUsingContext( &p );
render( context, width, height, distanceMin, distanceMax, zMin, zMax );
render( context, width, height, distanceMin, distanceMax, zMin, zMax, sourceId );
p.end();
return res;
}
void QgsProfilePlotRenderer::render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax )
void QgsProfilePlotRenderer::render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId )
{
QPainter *painter = context.painter();
if ( !painter )
@ -221,7 +249,7 @@ void QgsProfilePlotRenderer::render( QgsRenderContext &context, double width, do
for ( const ProfileJob &job : mJobs )
{
if ( job.complete && job.results )
if ( job.complete && job.results && ( sourceId.isEmpty() || job.generator->sourceId() == sourceId ) )
job.results->renderResults( profileRenderContext );
}
}
@ -272,3 +300,4 @@ void QgsProfilePlotRenderer::generateProfileStatic( ProfileJob &job )
job.results.reset( job.generator->takeResults() );
job.complete = true;
}

View File

@ -70,6 +70,11 @@ class CORE_EXPORT QgsProfilePlotRenderer : public QObject
~QgsProfilePlotRenderer() override;
/**
* Returns the ordered list of source IDs for the sources used by the renderer.
*/
QStringList sourceIds() const;
/**
* Start the generation job and immediately return.
* Does nothing if the generation is already in progress.
@ -95,6 +100,13 @@ class CORE_EXPORT QgsProfilePlotRenderer : public QObject
//! Returns TRUE if the generation job is currently running in background.
bool isActive() const;
/**
* Replaces the existing source with matching ID.
*
* The matching stored source will be deleted and replaced with \a source.
*/
void replaceSource( QgsAbstractProfileSource *source );
/**
* Invalidates the profile results from the source with matching ID.
*
@ -122,13 +134,17 @@ class CORE_EXPORT QgsProfilePlotRenderer : public QObject
/**
* Renders a portion of the profile to an image with the given \a width and \a height.
*
* If \a sourceId is empty then all sources will be rendered, otherwise only the matching source will be rendered.
*/
QImage renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax );
QImage renderToImage( int width, int height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId = QString() );
/**
* Renders a portion of the profile using the specified render \a context.
*
* If \a sourceId is empty then all sources will be rendered, otherwise only the matching source will be rendered.
*/
void render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax );
void render( QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId = QString() );
/**
* Snap a \a point to the results.
@ -154,6 +170,7 @@ class CORE_EXPORT QgsProfilePlotRenderer : public QObject
};
static void generateProfileStatic( ProfileJob &job );
bool replaceSourceInternal( QgsAbstractProfileSource *source, bool clearPreviousResults );
std::vector< std::unique_ptr< QgsAbstractProfileGenerator > > mGenerators;
QgsProfileRequest mRequest;

View File

@ -135,6 +135,13 @@ QgsProfileSnapResult QgsMeshLayerProfileResults::snapPoint( const QgsProfilePoin
return result;
}
void QgsMeshLayerProfileResults::updateFromGenerator( const QgsAbstractProfileGenerator *generator )
{
const QgsMeshLayerProfileGenerator *mlGenerator = qgis::down_cast< const QgsMeshLayerProfileGenerator * >( generator );
lineSymbol.reset( mlGenerator->mLineSymbol->clone() );
}
//
// QgsMeshLayerProfileGenerator
//

View File

@ -63,6 +63,7 @@ class CORE_EXPORT QgsMeshLayerProfileResults : public QgsAbstractProfileResults
QVector< QgsGeometry > asGeometries() const override;
void renderResults( QgsProfileRenderContext &context ) override;
QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context ) override;
void updateFromGenerator( const QgsAbstractProfileGenerator *generator ) override;
};
@ -116,6 +117,8 @@ class CORE_EXPORT QgsMeshLayerProfileGenerator : public QgsAbstractProfileGenera
std::unique_ptr< QgsMeshLayerProfileResults > mResults;
friend class QgsMeshLayerProfileResults;
};

View File

@ -138,6 +138,13 @@ void QgsRasterLayerProfileResults::renderResults( QgsProfileRenderContext &conte
lineSymbol->stopRender( context.renderContext() );
}
void QgsRasterLayerProfileResults::updateFromGenerator( const QgsAbstractProfileGenerator *generator )
{
const QgsRasterLayerProfileGenerator *rlGenerator = qgis::down_cast< const QgsRasterLayerProfileGenerator * >( generator );
lineSymbol.reset( rlGenerator->mLineSymbol->clone() );
}
//
// QgsRasterLayerProfileGenerator

View File

@ -62,6 +62,7 @@ class CORE_EXPORT QgsRasterLayerProfileResults : public QgsAbstractProfileResult
QVector< QgsGeometry > asGeometries() const override;
QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context ) override;
void renderResults( QgsProfileRenderContext &context ) override;
void updateFromGenerator( const QgsAbstractProfileGenerator *generator ) override;
};
/**
@ -113,6 +114,8 @@ class CORE_EXPORT QgsRasterLayerProfileGenerator : public QgsAbstractProfileGene
double mStepDistance = std::numeric_limits<double>::quiet_NaN();
friend class QgsRasterLayerProfileResults;
};
#endif // QGSRASTERLAYERPROFILEGENERATOR_H

View File

@ -398,6 +398,16 @@ void QgsVectorLayerProfileResults::renderResults( QgsProfileRenderContext &conte
}
}
void QgsVectorLayerProfileResults::updateFromGenerator( const QgsAbstractProfileGenerator *generator )
{
const QgsVectorLayerProfileGenerator *vlGenerator = qgis::down_cast< const QgsVectorLayerProfileGenerator * >( generator );
respectLayerSymbology = vlGenerator->mRespectLayerSymbology;
profileLineSymbol.reset( vlGenerator->mProfileLineSymbol->clone() );
profileFillSymbol.reset( vlGenerator->mProfileFillSymbol->clone() );
profileMarkerSymbol.reset( vlGenerator->mProfileMarkerSymbol->clone() );
}
//
// QgsVectorLayerProfileGenerator
//

View File

@ -85,6 +85,7 @@ class CORE_EXPORT QgsVectorLayerProfileResults : public QgsAbstractProfileResult
QVector< QgsGeometry > asGeometries() const override;
QgsProfileSnapResult snapPoint( const QgsProfilePoint &point, const QgsProfileSnapContext &context ) override;
void renderResults( QgsProfileRenderContext &context ) override;
void updateFromGenerator( const QgsAbstractProfileGenerator *generator ) override;
};
@ -168,6 +169,8 @@ class CORE_EXPORT QgsVectorLayerProfileGenerator : public QgsAbstractProfileGene
// NOT for use in the background thread!
QPointer< QgsVectorLayer > mLayer;
friend class QgsVectorLayerProfileResults;
};
#endif // QGSVECTORLAYERPROFILEGENERATOR_H

View File

@ -64,6 +64,7 @@ class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
setPos( mRect.topLeft() );
mImage = QImage();
mCachedImages.clear();
mPlotArea = QRectF();
update();
}
@ -71,10 +72,22 @@ class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
void updatePlot()
{
mImage = QImage();
mCachedImages.clear();
mPlotArea = QRectF();
update();
}
bool redrawResults( const QString &sourceId )
{
auto it = mCachedImages.find( sourceId );
if ( it == mCachedImages.end() )
return false;
mCachedImages.erase( it );
mImage = QImage();
return true;
}
QRectF boundingRect() const override
{
return mRect;
@ -92,7 +105,7 @@ class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
return mPlotArea;
}
QgsProfilePoint canvasPointToPlotPoint( const QPointF &point )
QgsProfilePoint canvasPointToPlotPoint( QPointF point )
{
if ( !mPlotArea.contains( point.x(), point.y() ) )
return QgsProfilePoint();
@ -119,8 +132,22 @@ class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
if ( !mRenderer )
return;
const QImage plot = mRenderer->renderToImage( plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum() );
rc.painter()->drawImage( plotArea.left(), plotArea.top(), plot );
const QStringList sourceIds = mRenderer->sourceIds();
for ( const QString &source : sourceIds )
{
QImage plot;
auto it = mCachedImages.constFind( source );
if ( it != mCachedImages.constEnd() )
{
plot = it.value();
}
else
{
plot = mRenderer->renderToImage( plotArea.width(), plotArea.height(), xMinimum(), xMaximum(), yMinimum(), yMaximum(), source );
mCachedImages.insert( source, plot );
}
rc.painter()->drawImage( plotArea.left(), plotArea.top(), plot );
}
}
void paint( QPainter *painter ) override
@ -149,6 +176,9 @@ class QgsElevationProfilePlotItem : public Qgs2DPlot, public QgsPlotCanvasItem
private:
QImage mImage;
QMap< QString, QImage > mCachedImages;
QRectF mRect;
QRectF mPlotArea;
QgsProfilePlotRenderer *mRenderer = nullptr;
@ -290,6 +320,11 @@ QgsElevationProfileCanvas::QgsElevationProfileCanvas( QWidget *parent )
mDeferredUpdateTimer->stop();
connect( mDeferredUpdateTimer, &QTimer::timeout, this, &QgsElevationProfileCanvas::startDeferredUpdate );
mDeferredRedrawTimer = new QTimer( this );
mDeferredRedrawTimer->setSingleShot( true );
mDeferredRedrawTimer->stop();
connect( mDeferredRedrawTimer, &QTimer::timeout, this, &QgsElevationProfileCanvas::startDeferredRedraw );
}
QgsElevationProfileCanvas::~QgsElevationProfileCanvas()
@ -375,11 +410,13 @@ void QgsElevationProfileCanvas::setupLayerConnections( QgsMapLayer *layer, bool
if ( isDisconnect )
{
disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
disconnect( layer->elevationProperties(), &QgsMapLayerElevationProperties::renderingPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
disconnect( layer, &QgsMapLayer::dataChanged, this, &QgsElevationProfileCanvas::updateResultsForLayer );
}
else
{
connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged );
connect( layer->elevationProperties(), &QgsMapLayerElevationProperties::renderingPropertyChanged, this, &QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged );
connect( layer, &QgsMapLayer::dataChanged, this, &QgsElevationProfileCanvas::updateResultsForLayer );
}
@ -412,7 +449,6 @@ void QgsElevationProfileCanvas::setupLayerConnections( QgsMapLayer *layer, bool
case QgsMapLayerType::PointCloudLayer:
case QgsMapLayerType::GroupLayer:
break;
}
}
@ -622,6 +658,27 @@ void QgsElevationProfileCanvas::onLayerProfileGenerationPropertyChanged()
}
}
void QgsElevationProfileCanvas::onLayerProfileRendererPropertyChanged()
{
// TODO -- handle nicely when existing job is in progress
if ( !mCurrentJob || mCurrentJob->isActive() )
return;
QgsMapLayerElevationProperties *properties = qobject_cast< QgsMapLayerElevationProperties * >( sender() );
if ( !properties )
return;
if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( properties->parent() ) )
{
if ( QgsAbstractProfileSource *source = dynamic_cast< QgsAbstractProfileSource * >( layer ) )
{
mCurrentJob->replaceSource( source );
}
if ( mPlotItem->redrawResults( layer->id() ) )
scheduleDeferredRedraw();
}
}
void QgsElevationProfileCanvas::updateResultsForLayer()
{
if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( sender() ) )
@ -643,6 +700,15 @@ void QgsElevationProfileCanvas::scheduleDeferredUpdate()
}
}
void QgsElevationProfileCanvas::scheduleDeferredRedraw()
{
if ( !mDeferredRedrawScheduled )
{
mDeferredRedrawTimer->start( 1 );
mDeferredRedrawScheduled = true;
}
}
void QgsElevationProfileCanvas::startDeferredUpdate()
{
if ( mCurrentJob && !mCurrentJob->isActive() )
@ -653,6 +719,12 @@ void QgsElevationProfileCanvas::startDeferredUpdate()
mDeferredUpdateScheduled = false;
}
void QgsElevationProfileCanvas::startDeferredRedraw()
{
mPlotItem->update();
mDeferredRedrawScheduled = false;
}
QgsProfilePoint QgsElevationProfileCanvas::canvasPointToPlotPoint( QPointF point ) const
{
if ( !mPlotItem->plotArea().contains( point.x(), point.y() ) )

View File

@ -205,9 +205,12 @@ class GUI_EXPORT QgsElevationProfileCanvas : public QgsPlotCanvas
void generationFinished();
void onLayerProfileGenerationPropertyChanged();
void onLayerProfileRendererPropertyChanged();
void updateResultsForLayer();
void scheduleDeferredUpdate();
void scheduleDeferredRedraw();
void startDeferredUpdate();
void startDeferredRedraw();
private:
@ -236,6 +239,8 @@ class GUI_EXPORT QgsElevationProfileCanvas : public QgsPlotCanvas
QgsProfilePlotRenderer *mCurrentJob = nullptr;
QTimer *mDeferredUpdateTimer = nullptr;
bool mDeferredUpdateScheduled = false;
QTimer *mDeferredRedrawTimer = nullptr;
bool mDeferredRedrawScheduled = false;
std::unique_ptr< QgsCurve > mProfileCurve;
double mTolerance = 0;