25#include <QtConcurrentMap>
27#include "moc_qgsprofilerenderer.cpp"
36 if ( std::unique_ptr< QgsAbstractProfileGenerator > generator { source->createProfileGenerator( mRequest ) } )
37 mGenerators.emplace_back( std::move( generator ) );
43 : mGenerators( std::move( generators ) )
58 res.reserve( mGenerators.size() );
59 for (
const auto &it : mGenerators )
61 res.append( it->sourceId() );
73 Q_ASSERT( mJobs.empty() );
75 mJobs.reserve( mGenerators.size() );
76 for (
const auto &it : mGenerators )
78 auto job = std::make_unique< ProfileJob >();
79 job->generator = it.get();
80 job->context = mContext;
81 mJobs.emplace_back( std::move( job ) );
84 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsProfilePlotRenderer::onGeneratingFinished );
86 mFuture = QtConcurrent::map( mJobs, generateProfileStatic );
87 mFutureWatcher.setFuture( mFuture );
97 Q_ASSERT( mJobs.empty() );
98 mJobs.reserve( mGenerators.size() );
100 for (
const auto &it : mGenerators )
102 auto job = std::make_unique< ProfileJob >();
103 job->generator = it.get();
104 job->context = mContext;
105 it.get()->generateProfile( job->context );
106 job->results.reset( job->generator->takeResults() );
107 job->complete =
true;
108 job->invalidatedResults.reset();
109 mJobs.emplace_back( std::move( job ) );
120 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsProfilePlotRenderer::onGeneratingFinished );
122 for (
const auto &job : mJobs )
124 if ( job->generator )
126 if (
QgsFeedback *feedback = job->generator->feedback() )
133 mFutureWatcher.waitForFinished();
135 onGeneratingFinished();
143 for (
const auto &job : mJobs )
145 if ( job->generator )
147 if (
QgsFeedback *feedback = job->generator->feedback() )
160 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsProfilePlotRenderer::onGeneratingFinished );
161 mFutureWatcher.waitForFinished();
163 onGeneratingFinished();
168 return mStatus != Idle;
173 if ( mContext == context )
177 const double distanceRangeChanged = context.
distanceRange() != mContext.distanceRange();
178 const double elevationRangeChanged = context.
elevationRange() != mContext.elevationRange();
181 for (
auto &job : mJobs )
187 if ( !jobNeedsRegeneration )
191 job->context = mContext;
192 if ( job->results && job->complete )
193 job->invalidatedResults = std::move( job->results );
194 job->results.reset();
195 job->complete =
false;
202 for (
auto &job : mJobs )
208 if ( !jobNeedsRegeneration )
212 job->context = mContext;
213 if ( job->results && job->complete )
214 job->invalidatedResults = std::move( job->results );
215 job->results.reset();
216 job->complete =
false;
223 replaceSourceInternal( source,
false );
228 return replaceSourceInternal( source,
true );
240 QString sourceId = generator->sourceId();
242 for (
auto &job : mJobs )
244 if ( job->generator && job->generator->sourceId() == sourceId )
248 if ( clearPreviousResults || job->generator->type() != generator->type() )
250 job->results.reset();
251 job->complete =
false;
253 else if ( job->results )
255 job->results->copyPropertiesFromGenerator( generator.get() );
257 job->generator = generator.get();
260 for (
auto it = mGenerators.begin(); it != mGenerators.end(); )
262 if ( ( *it )->sourceId() == sourceId )
263 it = mGenerators.erase( it );
267 mGenerators.emplace_back( std::move( generator ) );
278 mStatus = Generating;
280 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsProfilePlotRenderer::onGeneratingFinished );
282 mFuture = QtConcurrent::map( mJobs, generateProfileStatic );
283 mFutureWatcher.setFuture( mFuture );
288 double min = std::numeric_limits< double >::max();
289 double max = std::numeric_limits< double >::lowest();
290 for (
const auto &job : mJobs )
292 if ( job->complete && job->results )
295 min = std::min( min, jobRange.
lower() );
296 max = std::max( max, jobRange.
upper() );
304 QImage res( width, height, QImage::Format_ARGB32_Premultiplied );
305 res.setDotsPerMeterX( 96 / 25.4 * 1000 );
306 res.setDotsPerMeterY( 96 / 25.4 * 1000 );
307 res.fill( Qt::transparent );
315 const double mapUnitsPerPixel = ( distanceMax - distanceMin ) / width;
318 render( context, width, height, distanceMin, distanceMax, zMin, zMax, sourceId );
324QTransform QgsProfilePlotRenderer::computeRenderTransform(
double width,
double height,
double distanceMin,
double distanceMax,
double zMin,
double zMax )
326 QTransform transform;
327 transform.translate( 0, height );
328 transform.scale( width / ( distanceMax - distanceMin ), -height / ( zMax - zMin ) );
329 transform.translate( -distanceMin, -zMin );
336 QPainter *painter = context.
painter();
341 profileRenderContext.
setWorldTransform( computeRenderTransform( width, height, distanceMin, distanceMax, zMin, zMax ) );
346 for (
auto &job : mJobs )
348 if ( ( sourceId.isEmpty() || job->generator->sourceId() == sourceId ) )
351 if ( job->complete && job->results )
353 job->results->renderResults( profileRenderContext );
355 else if ( !job->complete && job->invalidatedResults )
358 job->invalidatedResults->renderResults( profileRenderContext );
364 QRectF plotArea( QPointF( 0, 0 ), QPointF( width, height ) );
370 auto subSections = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 255, 0, 0, 255 ), 0.5 );
371 subSections->setPenCapStyle( Qt::FlatCap );
372 return std::make_unique<QgsLineSymbol>(
QgsSymbolLayerList() << subSections.release() );
377 mSubsectionsSymbol.reset( symbol );
382 return mSubsectionsSymbol.get();
387 QgsCurve *profileCurve = mRequest.profileCurve();
388 if ( !profileCurve || profileCurve->
numPoints() < 3 || !mSubsectionsSymbol )
391 QTransform transform = computeRenderTransform( plotArea.width(), plotArea.height(), distanceMin, distanceMax, zMin, zMax );
394 profileCurve->
points( points );
395 QgsPoint firstPoint = points.takeFirst();
398 mSubsectionsSymbol->startRender( context );
399 double accumulatedDistance = 0.;
400 for (
const QgsPoint &point : points )
402 accumulatedDistance += point.distance( firstPoint );
403 QPointF output = transform.map( QPointF( accumulatedDistance, 0. ) );
404 QPolygonF polyLine( QVector<QPointF> { QPointF( output.x() + plotArea.left(), plotArea.top() ), QPointF( output.x() + plotArea.left(), plotArea.bottom() ) } );
405 mSubsectionsSymbol->renderPolyline( polyLine,
nullptr, context );
408 mSubsectionsSymbol->stopRender( context );
414 if ( !mRequest.profileCurve() )
415 return bestSnapResult;
417 double bestSnapDistance = std::numeric_limits< double >::max();
419 for (
const auto &job : mJobs )
422 if ( job->complete && job->results )
430 if ( snapDistance < bestSnapDistance )
432 bestSnapDistance = snapDistance;
433 bestSnapResult = jobSnapResult;
440 return bestSnapResult;
445 QVector<QgsProfileIdentifyResults> res;
446 if ( !mRequest.profileCurve() )
449 for (
const auto &job : mJobs )
452 if ( job->complete && job->results )
454 res.append( job->results->identify( point, context ) );
464 QVector<QgsProfileIdentifyResults> res;
465 if ( !mRequest.profileCurve() )
468 for (
const auto &job : mJobs )
471 if ( job->complete && job->results )
473 res.append( job->results->identify( distanceRange, elevationRange, context ) );
483 QVector<QgsAbstractProfileResults::Feature > res;
484 for (
const auto &job : mJobs )
490 if ( job->complete && job->results )
492 res.append( job->results->asFeatures( type, feedback ) );
499void QgsProfilePlotRenderer::onGeneratingFinished()
505void QgsProfilePlotRenderer::generateProfileStatic( std::unique_ptr< ProfileJob > &job )
510 Q_ASSERT( job->generator );
512 job->generator->generateProfile( job->context );
514 job->results.reset( job->generator->takeResults() );
515 job->complete =
true;
516 job->invalidatedResults.reset();
@ RespectsMaximumErrorMapUnit
Generated profile respects the QgsProfileGenerationContext::maximumErrorMapUnits() property.
@ RespectsElevationRange
Generated profile respects the QgsProfileGenerationContext::elevationRange() property.
@ RespectsDistanceRange
Generated profile respects the QgsProfileGenerationContext::distanceRange() property.
@ Antialiasing
Use antialiasing while drawing.
ProfileExportType
Types of export for elevation profiles.
Interface for classes which can generate elevation profiles.
virtual QgsAbstractProfileGenerator * createProfileGenerator(const QgsProfileRequest &request)=0
Given a profile request, returns a new profile generator ready for generating elevation profiles.
Abstract base class for curved geometry type.
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual void points(QgsPointSequence &pt) const =0
Returns a list of points within the curve.
QgsRange which stores a range of double values.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
A line symbol type, for rendering LineString and MultiLineString geometries.
Perform transforms between map coordinates and device coordinates.
Point geometry type, with support for z-dimension and m-values.
Encapsulates the context in which an elevation profile is to be generated.
double maximumErrorMapUnits() const
Returns the maximum allowed error in the generated result, in profile curve map units.
QgsDoubleRange elevationRange() const
Returns the range of elevations to include in the generation.
QgsDoubleRange distanceRange() const
Returns the range of distances to include in the generation.
Encapsulates the context of identifying profile results.
void setSubsectionsSymbol(QgsLineSymbol *symbol)
Sets the symbol used to draw the subsections.
QgsProfileSnapResult snapPoint(const QgsProfilePoint &point, const QgsProfileSnapContext &context)
Snap a point to the results.
void render(QgsRenderContext &context, double width, double height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId=QString())
Renders a portion of the profile using the specified render context.
void renderSubsectionsIndicator(QgsRenderContext &context, const QRectF &plotArea, double distanceMin, double distanceMax, double zMin, double zMax)
Renders the vertices of the profile curve as vertical lines using the specified render context.
void regenerateInvalidatedResults()
Starts a background regeneration of any invalidated results and immediately returns.
QVector< QgsAbstractProfileResults::Feature > asFeatures(Qgis::ProfileExportType type, QgsFeedback *feedback=nullptr)
Exports the profile results as a set of features.
QImage renderToImage(int width, int height, double distanceMin, double distanceMax, double zMin, double zMax, const QString &sourceId=QString(), double devicePixelRatio=1.0)
Renders a portion of the profile to an image with the given width and height.
void cancelGenerationWithoutBlocking()
Triggers cancellation of the generation job without blocking.
QgsLineSymbol * subsectionsSymbol()
Returns the line symbol used to draw the subsections.
void invalidateAllRefinableSources()
Invalidates previous results from all refinable sources.
void cancelGeneration()
Stop the generation job - does not return until the job has terminated.
QgsProfilePlotRenderer(const QList< QgsAbstractProfileSource * > &sources, const QgsProfileRequest &request)
Constructor for QgsProfilePlotRenderer, using the provided list of profile sources to generate the re...
void generateSynchronously()
Generate the profile results synchronously in this thread.
void startGeneration()
Start the generation job and immediately return.
QgsDoubleRange zRange() const
Returns the limits of the retrieved elevation values.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context)
Identify results visible at the specified profile point.
void waitForFinished()
Block until the current job has finished.
~QgsProfilePlotRenderer() override
bool isActive() const
Returns true if the generation job is currently running in background.
QStringList sourceIds() const
Returns the ordered list of source IDs for the sources used by the renderer.
bool invalidateResults(QgsAbstractProfileSource *source)
Invalidates the profile results from the source with matching ID.
void replaceSource(QgsAbstractProfileSource *source)
Replaces the existing source with matching ID.
void setContext(const QgsProfileGenerationContext &context)
Sets the context in which the profile generation will occur.
void generationFinished()
Emitted when the profile generation is finished (or canceled).
static std::unique_ptr< QgsLineSymbol > defaultSubSectionsSymbol()
Returns the default line symbol to use for subsections lines.
Encapsulates a point on a distance-elevation profile.
double elevation() const
Returns the elevation of the point.
double distance() const
Returns the distance of the point.
Abstract base class for storage of elevation profiles.
void setWorldTransform(const QTransform &transform)
Sets the transform from world coordinates to painter coordinates.
void setDistanceRange(const QgsDoubleRange &range)
Sets the range of distances to include in the render.
void setElevationRange(const QgsDoubleRange &range)
Sets the range of elevations to include in the render.
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
Encapsulates the context of snapping a profile point.
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
Encapsulates results of snapping a profile point.
bool isValid() const
Returns true if the result is a valid point.
QgsProfilePoint snappedPoint
Snapped point.
T lower() const
Returns the lower bound of the range.
T upper() const
Returns the upper bound of the range.
Contains information about the context of a rendering operation.
void setDevicePixelRatio(float ratio)
Sets the device pixel ratio.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QVector< QgsPoint > QgsPointSequence
QList< QgsSymbolLayer * > QgsSymbolLayerList