18#include "moc_qgsprofilerenderer.cpp" 
   25#include <QtConcurrentMap> 
   26#include <QtConcurrentRun> 
   36      if ( std::unique_ptr< QgsAbstractProfileGenerator > generator{ source->createProfileGenerator( mRequest ) } )
 
   37        mGenerators.emplace_back( std::move( generator ) );
 
 
   43  : mGenerators( std::move( generators ) )
 
 
   59  res.reserve( mGenerators.size() );
 
   60  for ( 
const auto &it : mGenerators )
 
   62    res.append( it->sourceId() );
 
 
   74  Q_ASSERT( mJobs.empty() );
 
   76  mJobs.reserve( mGenerators.size() );
 
   77  for ( 
const auto &it : mGenerators )
 
   79    auto job = std::make_unique< ProfileJob >();
 
   80    job->generator = it.get();
 
   81    job->context = mContext;
 
   82    mJobs.emplace_back( std::move( job ) );
 
   85  connect( &mFutureWatcher, &QFutureWatcher<void>::finished, 
this, &QgsProfilePlotRenderer::onGeneratingFinished );
 
   87  mFuture = QtConcurrent::map( mJobs, generateProfileStatic );
 
   88  mFutureWatcher.setFuture( mFuture );
 
 
   98  Q_ASSERT( mJobs.empty() );
 
   99  mJobs.reserve( mGenerators.size() );
 
  101  for ( 
const auto &it : mGenerators )
 
  103    auto job = std::make_unique< ProfileJob >();
 
  104    job->generator = it.get();
 
  105    job->context = mContext;
 
  106    it.get()->generateProfile( job->context );
 
  107    job->results.reset( job->generator->takeResults() );
 
  108    job->complete = 
true;
 
  109    job->invalidatedResults.reset();
 
  110    mJobs.emplace_back( std::move( job ) );
 
 
  121  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, 
this, &QgsProfilePlotRenderer::onGeneratingFinished );
 
  123  for ( 
const auto &job : mJobs )
 
  125    if ( job->generator )
 
  127      if ( 
QgsFeedback *feedback = job->generator->feedback() )
 
  134  mFutureWatcher.waitForFinished();
 
  136  onGeneratingFinished();
 
 
  144  for ( 
const auto &job : mJobs )
 
  146    if ( job->generator )
 
  148      if ( 
QgsFeedback *feedback = job->generator->feedback() )
 
 
  161  disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished, 
this, &QgsProfilePlotRenderer::onGeneratingFinished );
 
  162  mFutureWatcher.waitForFinished();
 
  164  onGeneratingFinished();
 
 
  169  return mStatus != Idle;
 
 
  174  if ( mContext == context )
 
  182  for ( 
auto &job : mJobs )
 
  188    if ( !jobNeedsRegeneration )
 
  192    job->context = mContext;
 
  193    if ( job->results && job->complete )
 
  194      job->invalidatedResults = std::move( job->results );
 
  195    job->results.reset();
 
  196    job->complete = 
false;
 
 
  203  for ( 
auto &job : mJobs )
 
  209    if ( !jobNeedsRegeneration )
 
  213    job->context = mContext;
 
  214    if ( job->results && job->complete )
 
  215      job->invalidatedResults = std::move( job->results );
 
  216    job->results.reset();
 
  217    job->complete = 
false;
 
 
  224  replaceSourceInternal( source, 
false );
 
 
  229  return replaceSourceInternal( source, 
true );
 
 
  241  QString sourceId = generator->
sourceId();
 
  243  for ( 
auto &job : mJobs )
 
  245    if ( job->generator && job->generator->sourceId() == sourceId )
 
  249      if ( clearPreviousResults )
 
  251        job->results.reset();
 
  252        job->complete = 
false;
 
  254      else if ( job->results )
 
  256        job->results->copyPropertiesFromGenerator( generator.get() );
 
  258      job->generator = generator.get();
 
  261      for ( 
auto it = mGenerators.begin(); it != mGenerators.end(); )
 
  263        if ( ( *it )->sourceId() == sourceId )
 
  264          it = mGenerators.erase( it );
 
  268      mGenerators.emplace_back( std::move( generator ) );
 
  279  mStatus = Generating;
 
  281  connect( &mFutureWatcher, &QFutureWatcher<void>::finished, 
this, &QgsProfilePlotRenderer::onGeneratingFinished );
 
  283  mFuture = QtConcurrent::map( mJobs, generateProfileStatic );
 
  284  mFutureWatcher.setFuture( mFuture );
 
 
  289  double min = std::numeric_limits< double >::max();
 
  290  double max = std::numeric_limits< double >::lowest();
 
  291  for ( 
const auto &job : mJobs )
 
  293    if ( job->complete && job->results )
 
  296      min = std::min( min, jobRange.
lower() );
 
  297      max = std::max( max, jobRange.
upper() );
 
 
  305  QImage res( width, height, QImage::Format_ARGB32_Premultiplied );
 
  306  res.setDotsPerMeterX( 96 / 25.4 * 1000 );
 
  307  res.setDotsPerMeterY( 96 / 25.4 * 1000 );
 
  308  res.fill( Qt::transparent );
 
  316  const double mapUnitsPerPixel = ( distanceMax - distanceMin ) / width;
 
  319  render( context, width, height, distanceMin, distanceMax, zMin, zMax, sourceId );
 
  320  QRectF plotArea( QPointF( 0, 0 ), QPointF( width, height ) );
 
 
  327QTransform QgsProfilePlotRenderer::computeRenderTransform( 
double width, 
double height, 
double distanceMin, 
double distanceMax, 
double zMin, 
double zMax )
 
  329  QTransform transform;
 
  330  transform.translate( 0, height );
 
  331  transform.scale( width / ( distanceMax - distanceMin ), -height / ( zMax - zMin ) );
 
  332  transform.translate( -distanceMin, -zMin );
 
  339  QPainter *painter = context.
painter();
 
  344  profileRenderContext.
setWorldTransform( computeRenderTransform( width, height, distanceMin, distanceMax, zMin, zMax ) );
 
  349  for ( 
auto &job : mJobs )
 
  351    if ( ( sourceId.isEmpty() || job->generator->sourceId() == sourceId ) )
 
  354      if ( job->complete && job->results )
 
  356        job->results->renderResults( profileRenderContext );
 
  358      else if ( !job->complete && job->invalidatedResults )
 
  361        job->invalidatedResults->renderResults( profileRenderContext );
 
 
  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();
 
 
  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 );
 
 
  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;
 
  449  for ( 
const auto &job : mJobs )
 
  452    if ( job->complete && job->results )
 
  454      res.append( job->results->identify( point, context ) );
 
 
  464  QVector<QgsProfileIdentifyResults> res;
 
  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.
 
virtual QString sourceId() const =0
Returns a unique identifier representing the source of the profile.
 
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...
 
QgsCurve * profileCurve() const
Returns the cross section profile curve, which represents the line along which the profile should be ...
 
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