QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgspointcloudlayerprofilegenerator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerprofilegenerator.cpp
3 ---------------
4 begin : April 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18
19#include "delaunator.hpp"
21#include "qgscurve.h"
22#include "qgsgeometry.h"
23#include "qgsgeometryutils.h"
24#include "qgsgeos.h"
25#include "qgsmessagelog.h"
27#include "qgspointcloudlayer.h"
31#include "qgsprofilepoint.h"
32#include "qgsprofilerequest.h"
33#include "qgsprofilesnapping.h"
34#include "qgsproject.h"
35#include "qgstriangle.h"
36
37#include <QString>
38
39using namespace Qt::StringLiterals;
40
41//
42// QgsPointCloudLayerProfileGenerator
43//
44
46{
47 mPointIndex = GEOSSTRtree_create_r( QgsGeosContext::get(), ( size_t ) 10 );
48}
49
51{
52 GEOSSTRtree_destroy_r( QgsGeosContext::get(), mPointIndex );
53 mPointIndex = nullptr;
54}
55
57{
58 GEOSContextHandle_t geosctxt = QgsGeosContext::get();
59
60 const std::size_t size = results.size();
61 PointResult *pointData = results.data();
62 for ( std::size_t i = 0; i < size; ++i, ++pointData )
63 {
64 if ( feedback->isCanceled() )
65 break;
66
67 geos::unique_ptr geosPoint( GEOSGeom_createPointFromXY_r( geosctxt, pointData->distanceAlongCurve, pointData->z ) );
68
69 GEOSSTRtree_insert_r( geosctxt, mPointIndex, geosPoint.get(), pointData );
70 // only required for GEOS < 3.9
71 }
72}
73
75{
76 return u"pointcloud"_s;
77}
78
80{
81 // TODO -- cache?
82 QMap< double, double > res;
83 for ( const PointResult &point : results )
84 {
85 res.insert( point.distanceAlongCurve, point.z );
86 }
87 return res;
88}
89
91{
92 // TODO -- cache?
94 res.reserve( results.size() );
95 for ( const PointResult &point : results )
96 {
97 res.append( QgsPoint( point.x, point.y, point.z ) );
98 }
99 return res;
100}
101
103{
104 // TODO -- cache?
105 QVector< QgsGeometry > res;
106 res.reserve( results.size() );
107 for ( const PointResult &point : results )
108 {
109 res.append( QgsGeometry( new QgsPoint( point.x, point.y, point.z ) ) );
110 }
111 return res;
112}
113
114QVector<QgsAbstractProfileResults::Feature> QgsPointCloudLayerProfileResults::asFeatures( Qgis::ProfileExportType type, QgsFeedback *feedback ) const
115{
116 QVector< QgsAbstractProfileResults::Feature > res;
117 res.reserve( static_cast< int >( results.size() ) );
118 switch ( type )
119 {
121 {
122 for ( const PointResult &point : results )
123 {
124 if ( feedback && feedback->isCanceled() )
125 break;
127 f.layerIdentifier = mLayerId;
128 f.geometry = QgsGeometry( std::make_unique< QgsPoint >( point.x, point.y, point.z ) );
129 res.append( f );
130 }
131 break;
132 }
133
135 {
136 for ( const PointResult &point : results )
137 {
138 if ( feedback && feedback->isCanceled() )
139 break;
141 f.layerIdentifier = mLayerId;
142 f.geometry = QgsGeometry( std::make_unique< QgsPoint >( point.distanceAlongCurve, point.z ) );
143 res.append( f );
144 }
145 break;
146 }
147
149 {
150 for ( const PointResult &point : results )
151 {
152 if ( feedback && feedback->isCanceled() )
153 break;
154
156 f.layerIdentifier = mLayerId;
157 f.attributes = { { u"distance"_s, point.distanceAlongCurve }, { u"elevation"_s, point.z } };
158 f.geometry = QgsGeometry( std::make_unique< QgsPoint >( point.x, point.y, point.z ) );
159 res << f;
160 }
161 break;
162 }
163 }
164
165 return res;
166}
167
172
174{
175 QPainter *painter = context.renderContext().painter();
176 if ( !painter )
177 return;
178
179 const QgsScopedQPainterState painterState( painter );
180
181 painter->setBrush( Qt::NoBrush );
182 painter->setPen( Qt::NoPen );
183
184 switch ( pointSymbol )
185 {
187 // for square point we always disable antialiasing -- it's not critical here and we benefit from the performance boost disabling it gives
188 context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );
189 break;
190
192 break;
193 }
194
195 const double minDistance = context.distanceRange().lower();
196 const double maxDistance = context.distanceRange().upper();
197 const double minZ = context.elevationRange().lower();
198 const double maxZ = context.elevationRange().upper();
199
200 const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
201 QPainterPath clipPath;
202 clipPath.addPolygon( context.worldTransform().map( visibleRegion ) );
203 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
204
205 const double penWidth = context.renderContext().convertToPainterUnits( pointSize, pointSizeUnit );
206
207 for ( const PointResult &point : std::as_const( results ) )
208 {
209 QPointF p = context.worldTransform().map( QPointF( point.distanceAlongCurve, point.z ) );
210 QColor color = respectLayerColors ? point.color : pointColor;
212 color.setAlphaF( color.alphaF() * ( 1.0 - std::pow( point.distanceFromCurve / tolerance, 0.5 ) ) );
213
214 switch ( pointSymbol )
215 {
217 painter->fillRect( QRectF( p.x() - penWidth * 0.5, p.y() - penWidth * 0.5, penWidth, penWidth ), color );
218 break;
219
221 painter->setBrush( QBrush( color ) );
222 painter->setPen( Qt::NoPen );
223 painter->drawEllipse( QRectF( p.x() - penWidth * 0.5, p.y() - penWidth * 0.5, penWidth, penWidth ) );
224 break;
225 }
226 }
227}
228
230{
231 QList< const QgsPointCloudLayerProfileResults::PointResult * > *list;
232};
233void _GEOSQueryCallback( void *item, void *userdata )
234{
235 reinterpret_cast<_GEOSQueryCallbackData *>( userdata )->list->append( reinterpret_cast<const QgsPointCloudLayerProfileResults::PointResult *>( item ) );
236}
237
239{
241
242 GEOSContextHandle_t geosctxt = QgsGeosContext::get();
243
244 const double minDistance = point.distance() - context.maximumPointDistanceDelta;
245 const double maxDistance = point.distance() + context.maximumPointDistanceDelta;
246 const double minElevation = point.elevation() - context.maximumPointElevationDelta;
247 const double maxElevation = point.elevation() + context.maximumPointElevationDelta;
248
249 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
250 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, minDistance, minElevation );
251 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, maxDistance, maxElevation );
252 geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
253
254 QList<const PointResult *> items;
255 struct _GEOSQueryCallbackData callbackData;
256 callbackData.list = &items;
257 GEOSSTRtree_query_r( geosctxt, mPointIndex, searchDiagonal.get(), _GEOSQueryCallback, &callbackData );
258 if ( items.empty() )
259 return result;
260
261 double bestMatchDistance = std::numeric_limits< double >::max();
262 const PointResult *bestMatch = nullptr;
263 for ( const PointResult *candidate : std::as_const( items ) )
264 {
265 const double distance = std::sqrt( std::pow( candidate->distanceAlongCurve - point.distance(), 2 ) + std::pow( ( candidate->z - point.elevation() ) / context.displayRatioElevationVsDistance, 2 ) );
266 if ( distance < bestMatchDistance )
267 {
268 bestMatchDistance = distance;
269 bestMatch = candidate;
270 }
271 }
272 if ( !bestMatch )
273 return result;
274
275 result.snappedPoint = QgsProfilePoint( bestMatch->distanceAlongCurve, bestMatch->z );
276 return result;
277}
278
279QVector<QgsProfileIdentifyResults> QgsPointCloudLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
280{
281 return identify(
284 context
285 );
286}
287
288QVector<QgsProfileIdentifyResults> QgsPointCloudLayerProfileResults::identify( const QgsDoubleRange &distanceRange, const QgsDoubleRange &elevationRange, const QgsProfileIdentifyContext &context )
289{
290 if ( !mLayer )
291 return {};
292
293 std::unique_ptr< QgsCurve > substring( mProfileCurve->curveSubstring( distanceRange.lower(), distanceRange.upper() ) );
294 QgsGeos substringGeos( substring.get() );
295 std::unique_ptr< QgsAbstractGeometry > searchGeometry( substringGeos.buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
296 if ( !searchGeometry )
297 return {};
298
299 const QgsCoordinateTransform curveToLayerTransform = QgsCoordinateTransform( mCurveCrs, mLayer->crs(), context.project ? context.project->transformContext() : QgsCoordinateTransformContext() );
300 try
301 {
302 searchGeometry->transform( curveToLayerTransform );
303 }
304 catch ( QgsCsException & )
305 {
306 return {};
307 }
308
309 // we have to adjust the elevation range to "undo" the z offset/z scale before we can hand to the provider
310 const QgsDoubleRange providerElevationRange( ( elevationRange.lower() - mZOffset ) / mZScale, ( elevationRange.upper() - mZOffset ) / mZScale );
311
312 const QgsGeometry pointCloudSearchGeometry( std::move( searchGeometry ) );
313 const QVector<QVariantMap> pointAttributes = mLayer->dataProvider()->identify( mMaxErrorInLayerCoordinates, pointCloudSearchGeometry, providerElevationRange );
314 if ( pointAttributes.empty() )
315 return {};
316
317 return { QgsProfileIdentifyResults( mLayer, pointAttributes ) };
318}
319
321{
322 const QgsPointCloudLayerProfileGenerator *pcGenerator = qgis::down_cast< const QgsPointCloudLayerProfileGenerator *>( generator );
323 tolerance = pcGenerator->mTolerance;
324 pointSize = pcGenerator->mPointSize;
325 pointSizeUnit = pcGenerator->mPointSizeUnit;
326 pointSymbol = pcGenerator->mPointSymbol;
327 pointColor = pcGenerator->mPointColor;
328 respectLayerColors = static_cast< bool >( pcGenerator->mRenderer );
329 opacityByDistanceEffect = pcGenerator->mOpacityByDistanceEffect;
330
331 mLayer = pcGenerator->mLayer;
332 mLayerId = pcGenerator->mId;
333 mCurveCrs = pcGenerator->mTargetCrs;
334 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
335 mTolerance = pcGenerator->mTolerance;
336
337 mZOffset = pcGenerator->mZOffset;
338 mZScale = pcGenerator->mZScale;
339}
340
341//
342// QgsPointCloudLayerProfileGeneratorBase
343//
344
346 : mLayer( layer )
347 , mIndex( layer->index() )
348 , mSubIndexes( layer->dataProvider()->subIndexes() )
349 , mLayerAttributes( layer->attributes() )
350 , mRenderer( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->respectLayerColors() && mLayer->renderer() ? mLayer->renderer()->clone() : nullptr )
351 , mMaximumScreenError( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->maximumScreenError() )
352 , mMaximumScreenErrorUnit( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->maximumScreenErrorUnit() )
353 , mId( layer->id() )
354 , mFeedback( std::make_unique< QgsFeedback >() )
355 , mTolerance( request.tolerance() )
356 , mSourceCrs( layer->crs3D() )
357 , mTargetCrs( request.crs() )
358 , mTransformContext( request.transformContext() )
359 , mStepDistance( request.stepDistance() )
360 , mZOffset( layer->elevationProperties()->zOffset() )
361 , mZScale( layer->elevationProperties()->zScale() )
362 , mLayerToTargetTransform( layer->crs3D(), request.crs(), request.transformContext() )
363 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
364{}
365
367
368bool QgsPointCloudLayerProfileGeneratorBase::collectData( QgsGeos &curve, const double mapUnitsPerPixel, const double maximumErrorPixels, const QgsDoubleRange &zRange, double &maxErrorInLayerCoordinates )
369{
370 maxErrorInLayerCoordinates = 0;
371
372 QVector<QgsPointCloudIndex> indexes;
373 if ( mIndex && mIndex.isValid() )
374 indexes.append( mIndex );
375
376 // Gather all relevant sub-indexes
377 const QgsRectangle profileCurveBbox = mProfileCurve->boundingBox();
378 for ( const QgsPointCloudSubIndex &subidx : mSubIndexes )
379 {
380 QgsPointCloudIndex index = subidx.index();
381 if ( index && index.isValid() && subidx.polygonBounds().intersects( profileCurveBbox ) )
382 indexes.append( subidx.index() );
383 }
384
385 if ( indexes.empty() )
386 return false;
387
388 std::unique_ptr< QgsAbstractGeometry > searchGeometryInLayerCrs;
389 searchGeometryInLayerCrs.reset( curve.buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
391
392 try
393 {
394 searchGeometryInLayerCrs->transform( mLayerToTargetTransform, Qgis::TransformDirection::Reverse );
395 }
396 catch ( QgsCsException & )
397 {
398 QgsDebugError( u"Error transforming profile line to layer CRS"_s );
399 return false;
400 }
401
402 if ( mFeedback->isCanceled() )
403 return false;
404
405 mSearchGeometryInLayerCrsGeometryEngine = std::make_unique< QgsGeos >( searchGeometryInLayerCrs.get() );
407 const QgsRectangle maxSearchExtentInLayerCrs = searchGeometryInLayerCrs->boundingBox();
408
409 QgsPointCloudRequest request;
414
415 if ( mRenderer )
416 {
417 mPreparedRendererData = mRenderer->prepare();
419 {
420 const QSet< QString > rendererAttributes = mPreparedRendererData->usedAttributes();
421 for ( const QString &attribute : std::as_const( rendererAttributes ) )
422 {
423 if ( attributes.indexOf( attribute ) >= 0 )
424 continue; // don't re-add attributes we are already going to fetch
425
426 const int layerIndex = mLayerAttributes.indexOf( attribute );
427 if ( layerIndex < 0 )
428 {
429 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
430 continue;
431 }
432
433 attributes.push_back( mLayerAttributes.at( layerIndex ) );
434 }
435 }
436 }
437 else
438 {
439 mPreparedRendererData.reset();
440 }
441
442 request.setAttributes( attributes );
443
445 extentTransform.setBallparkTransformsAreAppropriate( true );
446
447
448 for ( QgsPointCloudIndex pc : std::as_const( indexes ) )
449 {
450 const QgsPointCloudNode root = pc.getNode( pc.root() );
451 const QgsRectangle rootNodeExtentLayerCoords = root.bounds().toRectangle();
452 QgsRectangle rootNodeExtentInCurveCrs;
453 try
454 {
455 rootNodeExtentInCurveCrs = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
456 }
457 catch ( QgsCsException & )
458 {
459 QgsDebugError( u"Could not transform node extent to curve CRS"_s );
460 rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords;
461 }
462
463 const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.width() / pc.span(); // in curve coords
464 if ( rootErrorInMapCoordinates < 0.0 )
465 {
466 QgsDebugError( u"Invalid root node error"_s );
467 return false;
468 }
469
470 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
471 gatherPoints( pc, request, maximumErrorPixels, rootErrorPixels, zRange, maxSearchExtentInLayerCrs );
472
473 if ( mFeedback->isCanceled() )
474 return false;
475
476 const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.width() / pc.span();
477 const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel;
478
479 maxErrorInLayerCoordinates = std::max( maxErrorInLayerCoordinates, maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates );
480
481 if ( mFeedback->isCanceled() )
482 return false;
483 }
484
485 return true;
486}
487
489 QgsPointCloudIndex &pc, QgsPointCloudNodeId n, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent
490)
491{
492 QVector<QgsPointCloudNodeId> nodes;
493
494 if ( mFeedback->isCanceled() )
495 {
496 return nodes;
497 }
498
499 QgsPointCloudNode node = pc.getNode( n );
500 QgsBox3D nodeBounds = node.bounds();
501 const QgsDoubleRange nodeZRange( nodeBounds.zMinimum(), nodeBounds.zMaximum() );
502 if ( !zRange.isInfinite() && !zRange.overlaps( nodeZRange ) )
503 return nodes;
504
505 if ( !searchExtent.intersects( nodeBounds.toRectangle() ) )
506 return nodes;
507
508 const QgsGeometry nodeMapGeometry = QgsGeometry::fromRect( nodeBounds.toRectangle() );
509 if ( !mSearchGeometryInLayerCrsGeometryEngine->intersects( nodeMapGeometry.constGet() ) )
510 return nodes;
511
512 if ( node.pointCount() > 0 )
513 nodes.append( n );
514
515 double childrenErrorPixels = nodeErrorPixels / 2.0;
516 if ( childrenErrorPixels < maxErrorPixels )
517 return nodes;
518
519 for ( const QgsPointCloudNodeId &nn : node.children() )
520 {
521 nodes += traverseTree( pc, nn, maxErrorPixels, childrenErrorPixels, zRange, searchExtent );
522 }
523
524 return nodes;
525}
526
527int QgsPointCloudLayerProfileGeneratorBase::visitNodesSync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
528{
529 int nodesDrawn = 0;
530 for ( const QgsPointCloudNodeId &n : nodes )
531 {
532 if ( mFeedback->isCanceled() )
533 break;
534
535 std::unique_ptr<QgsPointCloudBlock> block( pc.nodeData( n, request ) );
536
537 if ( !block )
538 continue;
539
540 visitBlock( block.get(), zRange );
541
542 ++nodesDrawn;
543 }
544 return nodesDrawn;
545}
546
547int QgsPointCloudLayerProfileGeneratorBase::visitNodesAsync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
548{
549 if ( nodes.isEmpty() )
550 return 0;
551
552 int nodesDrawn = 0;
553
554 // see notes about this logic in QgsPointCloudLayerRenderer::renderNodesAsync
555
556 // Async loading of nodes
557 QVector<QgsPointCloudBlockRequest *> blockRequests;
558 QEventLoop loop;
559 QObject::connect( mFeedback.get(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
560
561 for ( int i = 0; i < nodes.size(); ++i )
562 {
563 const QgsPointCloudNodeId &n = nodes[i];
564 const QString nStr = n.toString();
565 QgsPointCloudBlockRequest *blockRequest = pc.asyncNodeData( n, request );
566 blockRequests.append( blockRequest );
567 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop, [this, &nodesDrawn, &loop, &blockRequests, &zRange, nStr, blockRequest]() {
568 blockRequests.removeOne( blockRequest );
569
570 // If all blocks are loaded, exit the event loop
571 if ( blockRequests.isEmpty() )
572 loop.exit();
573
574 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
575
576 blockRequest->deleteLater();
577
578 if ( mFeedback->isCanceled() )
579 {
580 return;
581 }
582
583 if ( !block )
584 {
585 QgsDebugError( u"Unable to load node %1, error: %2"_s.arg( nStr, blockRequest->errorStr() ) );
586 return;
587 }
588
589 visitBlock( block.get(), zRange );
590 ++nodesDrawn;
591 } );
592 }
593
594 // Wait for all point cloud nodes to finish loading
595 if ( !blockRequests.isEmpty() )
596 loop.exec();
597
598 // Generation may have got canceled and the event loop exited before finished()
599 // was called for all blocks, so let's clean up anything that is left
600 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
601 {
602 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
603 block.reset();
604 blockRequest->deleteLater();
605 }
606
607 return nodesDrawn;
608}
609
611 QgsPointCloudIndex &pc, QgsPointCloudRequest &request, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent
612)
613{
614 const QVector<QgsPointCloudNodeId> nodes = traverseTree( pc, pc.root(), maxErrorPixels, nodeErrorPixels, zRange, searchExtent );
615
616 if ( nodes.empty() )
617 {
618 return;
619 }
620
621 switch ( pc.accessType() )
622 {
624 {
625 visitNodesSync( nodes, pc, request, zRange );
626 break;
627 }
629 {
630 visitNodesAsync( nodes, pc, request, zRange );
631 break;
632 }
633 }
634}
635
636//
637// QgsPointCloudLayerProfileGenerator
638//
639
642 , mPointSize( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSize() )
643 , mPointSizeUnit( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSizeUnit() )
644 , mPointSymbol( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSymbol() )
645 , mPointColor( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointColor() )
646 , mOpacityByDistanceEffect( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->applyOpacityByDistanceEffect() )
647{}
648
650{
651 return mId;
652}
653
655{
656 return u"pointcloud"_s;
657}
658
663
665
667{
668 mGatheredPoints.clear();
669
670 const double startDistanceOffset = std::max( !context.distanceRange().isInfinite() ? context.distanceRange().lower() : 0, 0.0 );
671 const double endDistance = context.distanceRange().upper();
672
673 std::unique_ptr< QgsCurve > trimmedCurve;
674 QgsCurve *sourceCurve = nullptr;
675 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
676 {
677 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
678 sourceCurve = trimmedCurve.get();
679 }
680 else
681 {
682 sourceCurve = mProfileCurve.get();
683 }
684
685 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
686 if ( maximumErrorPixels < 0.0 )
687 {
688 QgsDebugError( u"Invalid maximum error in pixels"_s );
689 return false;
690 }
691
692 const double toleranceInPixels = context.convertDistanceToPixels( mTolerance, Qgis::RenderUnit::MapUnits );
693 // ensure that the maximum error is compatible with the tolerance size -- otherwise if the tolerance size
694 // is much smaller than the maximum error, we don't dig deep enough into the point cloud nodes to find
695 // points which are inside the tolerance.
696 // "4" is a magic number here, based purely on what "looks good" in the profile results!
697 if ( toleranceInPixels / 4 < maximumErrorPixels )
698 maximumErrorPixels = toleranceInPixels / 4;
699
700 double maxErrorInLayerCrs;
701
702 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
703 if ( mapUnitsPerPixel < 0.0 )
704 {
705 QgsDebugError( u"Invalid map units per pixel ratio"_s );
706 return false;
707 }
708
709 QgsGeos originalCurveGeos( sourceCurve );
710 originalCurveGeos.prepareGeometry();
711
712 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, context.elevationRange(), maxErrorInLayerCrs ) || mGatheredPoints.empty() )
713 {
714 mResults = nullptr;
715 return false;
716 }
717
718
719 mResults = std::make_unique< QgsPointCloudLayerProfileResults >();
720 mResults->copyPropertiesFromGenerator( this );
721 mResults->mMaxErrorInLayerCoordinates = maxErrorInLayerCrs;
722
723 // convert x/y values back to distance/height values
724 QString lastError;
725 const QgsPointCloudLayerProfileResults::PointResult *pointData = mGatheredPoints.constData();
726 const int size = mGatheredPoints.size();
727 mResults->results.resize( size );
728 QgsPointCloudLayerProfileResults::PointResult *destData = mResults->results.data();
729 for ( int i = 0; i < size; ++i, ++pointData, ++destData )
730 {
731 if ( mFeedback->isCanceled() )
732 return false;
733
734 *destData = *pointData;
735 destData->distanceAlongCurve = startDistanceOffset + originalCurveGeos.lineLocatePoint( destData->x, destData->y, &lastError );
736 if ( mOpacityByDistanceEffect ) // don't calculate this if we don't need it
737 destData->distanceFromCurve = originalCurveGeos.distance( destData->x, destData->y );
738
739 mResults->minZ = std::min( destData->z, mResults->minZ );
740 mResults->maxZ = std::max( destData->z, mResults->maxZ );
741 }
742 mResults->finalize( mFeedback.get() );
743
744 return true;
745}
746
751
756
757void QgsPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
758{
759 const char *ptr = block->data();
760 int count = block->pointCount();
761
762 const QgsPointCloudAttributeCollection request = block->attributes();
763
764 const std::size_t recordSize = request.pointRecordSize();
765
766 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
767 int xOffset = 0, yOffset = 0, zOffset = 0;
768 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
769 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
770 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
771
772 bool useRenderer = false;
774 {
775 useRenderer = mPreparedRendererData->prepareBlock( block );
776 }
777
778 QColor color;
779 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
780 for ( int i = 0; i < count; ++i )
781 {
782 if ( mFeedback->isCanceled() )
783 {
784 break;
785 }
786
787 QgsPointCloudLayerProfileResults::PointResult res;
788 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), res.x, res.y, res.z );
789
790 res.z = res.z * mZScale + mZOffset;
791 if ( !zRange.contains( res.z ) )
792 continue;
793
794 if ( useRenderer )
795 {
796 color = mPreparedRendererData->pointColor( block, i, res.z );
797 if ( !color.isValid() )
798 continue;
799
800 res.color = color.rgba();
801 }
802 else
803 {
804 res.color = mPointColor.rgba();
805 }
806
807 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x, res.y ) )
808 {
809 if ( reproject )
810 {
811 try
812 {
813 mLayerToTargetTransform.transformInPlace( res.x, res.y, res.z );
814 }
815 catch ( QgsCsException & )
816 {
817 continue;
818 }
819 }
820
821 mGatheredPoints.append( res );
822 }
823 }
824}
825
828{
829 mSymbology = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileSymbology();
830 mElevationLimit = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->elevationLimit();
831
832 mLineSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileLineSymbol()->clone() );
833 mFillSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileFillSymbol()->clone() );
834}
835
836void QgsTriangulatedPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
837{
838 const char *ptr = block->data();
839 int count = block->pointCount();
840
841 const QgsPointCloudAttributeCollection request = block->attributes();
842
843 const std::size_t recordSize = request.pointRecordSize();
844
845 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
846 int xOffset = 0, yOffset = 0, zOffset = 0;
847 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
848 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
849 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
850
851 bool useRenderer = false;
853 {
854 useRenderer = mPreparedRendererData->prepareBlock( block );
855 }
856
857 QColor color;
858 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
859 for ( int i = 0; i < count; ++i )
860 {
861 if ( mFeedback->isCanceled() )
862 {
863 break;
864 }
865
866 double x, y, z;
867 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
868 QgsPoint res( x, y, z * mZScale + mZOffset );
869 if ( !zRange.contains( res.z() ) )
870 continue;
871
872 if ( useRenderer )
873 {
874 color = mPreparedRendererData->pointColor( block, i, res.z() );
875 if ( !color.isValid() )
876 continue;
877 }
878
879 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x(), res.y() ) )
880 {
881 if ( reproject )
882 {
883 try
884 {
885 double x = res.x(), y = res.y(), z = res.z();
886 mLayerToTargetTransform.transformInPlace( x, y, z );
887 res.setX( x );
888 res.setY( y );
889 res.setZ( z );
890 }
891 catch ( QgsCsException & )
892 {
893 continue;
894 }
895 }
896
897 mGatheredPoints.append( res );
898 }
899 }
900}
901
906
908{
909 return u"triangulatedpointcloud"_s;
910}
911
913
915{
916 mGatheredPoints.clear();
917
918 QgsCurve *sourceCurve = mProfileCurve.get();
919
920 // we need to transform the profile curve and max search extent to the layer's CRS
921 QgsGeos originalCurveGeos( sourceCurve );
922 originalCurveGeos.prepareGeometry();
923
924 if ( mFeedback->isCanceled() )
925 return false;
926
927 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
928 if ( maximumErrorPixels < 0.0 )
929 {
930 QgsDebugError( u"Invalid maximum error in pixels"_s );
931 return false;
932 }
933
934 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
935 if ( mapUnitsPerPixel < 0.0 )
936 {
937 QgsDebugError( u"Invalid map units per pixel ratio"_s );
938 return false;
939 }
940
941 double maxErrorInLayerCrs;
942 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, QgsDoubleRange(), maxErrorInLayerCrs ) )
943 return false;
944
945 if ( mGatheredPoints.size() < 3 )
946 {
947 mResults = nullptr;
948 return false;
949 }
950
951 mResults = std::make_unique< QgsTriangulatedPointCloudLayerProfileResults >();
952 mResults->copyPropertiesFromGenerator( this );
953
954 std::unique_ptr<delaunator::Delaunator> triangulator;
955 std::vector<double> vertices;
956 std::vector<double> zValues;
957
958 vertices.reserve( mGatheredPoints.size() * 2 );
959 zValues.reserve( mGatheredPoints.size() );
960
961 for ( QgsPoint point : mGatheredPoints )
962 {
963 vertices.push_back( point.x() );
964 vertices.push_back( point.y() );
965 zValues.push_back( point.z() );
966 }
967
968 triangulator.reset( new delaunator::Delaunator( vertices ) );
969 const std::vector<size_t> &triangleIndexes = triangulator->triangles;
970
971 QgsPointSequence profilePoints;
972 QString lastError;
973 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
974 {
975 const size_t idx0 = triangleIndexes[i];
976 const size_t idx1 = triangleIndexes[i + 1];
977 const size_t idx2 = triangleIndexes[i + 2];
978
979 const QgsPoint pt0( vertices[2 * idx0], vertices[2 * idx0 + 1], zValues[idx0] );
980 const QgsPoint pt1( vertices[2 * idx1], vertices[2 * idx1 + 1], zValues[idx1] );
981 const QgsPoint pt2( vertices[2 * idx2], vertices[2 * idx2 + 1], zValues[idx2] );
982
983 const QgsTriangle triangle( pt0, pt1, pt2 );
984
985 QgsGeos geosPoly( &triangle );
986 geosPoly.prepareGeometry();
987
988 std::unique_ptr<QgsAbstractGeometry> intersectingGeom( geosPoly.intersection( sourceCurve ) );
989 if ( !intersectingGeom )
990 continue;
991
992 // probably not needed, but we may end up with something else, better to check
993 if ( QgsWkbTypes::flatType( intersectingGeom->wkbType() ) != Qgis::WkbType::LineString )
994 continue;
995
996 const QgsLineString *linestring = qgsgeometry_cast<const QgsLineString *>( intersectingGeom.get() );
997 for ( const QgsPoint &p : { linestring->startPoint(), linestring->endPoint() } )
998 {
999 const double z = QgsGeometryUtils::interpolateZ( pt0, pt1, pt2, p.x(), p.y() );
1000 if ( std::isnan( z ) )
1001 continue;
1002
1003 const double distanceAlong = originalCurveGeos.lineLocatePoint( p, &lastError );
1004 if ( std::isnan( distanceAlong ) )
1005 continue;
1006
1007 QgsPoint profilePoint( p.x(), p.y(), z, distanceAlong );
1008 profilePoints.append( profilePoint );
1009 }
1010
1011 if ( mFeedback->isCanceled() )
1012 return false;
1013 }
1014
1015 std::sort( profilePoints.begin(), profilePoints.end(), []( const QgsPoint &a, const QgsPoint &b ) { return a.m() < b.m(); } );
1016
1017 if ( mResults )
1018 {
1019 for ( const QgsPoint &pp : std::as_const( profilePoints ) )
1020 {
1021 const double z = pp.z();
1022 const double distanceAlong = pp.m();
1023
1024 mResults->minZ = std::min( z, mResults->minZ );
1025 mResults->maxZ = std::max( z, mResults->maxZ );
1026 mResults->mDistanceToHeightMap.insert( distanceAlong, z );
1027 mResults->mRawPoints.append( pp );
1028 }
1029 }
1030
1031 return true;
1032}
1033
1038
1043
1045{
1046 return u"triangulatedpointcloud"_s;
1047}
1048
1049QVector<QgsProfileIdentifyResults> QgsTriangulatedPointCloudLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
1050{
1051 const QVector<QgsProfileIdentifyResults> noLayerResults = QgsAbstractProfileSurfaceResults::identify( point, context );
1052
1053 // we have to make a new list, with the correct layer reference set
1054 QVector<QgsProfileIdentifyResults> res;
1055 res.reserve( noLayerResults.size() );
1056 for ( const QgsProfileIdentifyResults &result : noLayerResults )
1057 {
1058 res.append( QgsProfileIdentifyResults( mLayer, result.results() ) );
1059 }
1060 return res;
1061}
1062
1064{
1065 const QgsTriangulatedPointCloudLayerProfileGenerator *pcGenerator = qgis::down_cast< const QgsTriangulatedPointCloudLayerProfileGenerator * >( generator );
1066
1067 mLayer = pcGenerator->mLayer;
1068 mLayerId = pcGenerator->mId;
1069 mCurveCrs = pcGenerator->mTargetCrs;
1070 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
1071 mTolerance = pcGenerator->mTolerance;
1072
1073 mLineSymbol.reset( pcGenerator->mLineSymbol->clone() );
1074 mFillSymbol.reset( pcGenerator->mFillSymbol->clone() );
1075 symbology = pcGenerator->mSymbology;
1076 mElevationLimit = pcGenerator->mElevationLimit;
1077
1078 mZOffset = pcGenerator->mZOffset;
1079 mZScale = pcGenerator->mZScale;
1080}
1081
@ RespectsMaximumErrorMapUnit
Generated profile respects the QgsProfileGenerationContext::maximumErrorMapUnits() property.
Definition qgis.h:4360
@ RespectsDistanceRange
Generated profile respects the QgsProfileGenerationContext::distanceRange() property.
Definition qgis.h:4361
@ Circle
Renders points as circles.
Definition qgis.h:4389
@ Square
Renders points as squares.
Definition qgis.h:4388
QFlags< ProfileGeneratorFlag > ProfileGeneratorFlags
Definition qgis.h:4365
@ Round
Use rounded joins.
Definition qgis.h:2202
@ MapUnits
Map units.
Definition qgis.h:5342
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2190
@ Local
Local means the source is a local file on the machine.
Definition qgis.h:6433
@ Remote
Remote means it's loaded through a protocol like HTTP.
Definition qgis.h:6434
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4374
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4376
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4375
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4377
@ LineString
LineString.
Definition qgis.h:297
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2766
Abstract base class for objects which generate elevation profiles.
Abstract base class for storage of elevation profiles.
void renderResults(QgsProfileRenderContext &context) override
Renders the results to the specified context.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context) override
Identify results visible at the specified profile point.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:388
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QgsRange which stores a range of double values.
Definition qgsrange.h:217
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:266
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void canceled()
Internal routines can connect to this signal if they use event loop.
static double interpolateZ(const QgsPoint &a, const QgsPoint &b, const QgsPoint &c, double x, double y)
Interpolates the Z value at the given (x, y) location within the triangle defined by points a,...
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const override
Calculate the intersection of this and geom.
Definition qgsgeos.cpp:314
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition qgsgeos.cpp:2079
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:577
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition qgsgeos.cpp:286
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
Definition qgsgeos.cpp:3179
Line string geometry type, with support for z-dimension and m-values.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsPoint endPoint() const override
Returns the end point of the curve.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
int indexOf(const QString &name) const
Returns the index of the attribute with the specified name.
Attribute for point cloud data pair of name and size in bytes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
DataType type() const
Returns the data type.
Base class for handling loading QgsPointCloudBlock asynchronously.
QString errorStr() const
Returns the error message string of the request.
void finished()
Emitted when the request processing has finished.
std::unique_ptr< QgsPointCloudBlock > takeBlock()
Returns the requested block.
Base class for storing raw data from point cloud nodes.
QgsVector3D scale() const
Returns the custom scale of the block.
const char * data() const
Returns raw pointer to data.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes that are stored in the data block, along with their size.
int pointCount() const
Returns number of points that are stored in the block.
QgsVector3D offset() const
Returns the custom offset of the block.
Smart pointer for QgsAbstractPointCloudIndex.
QgsPointCloudBlockRequest * asyncNodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
bool isValid() const
Returns whether index is loaded and valid.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
QgsPointCloudNodeId root() const
Returns root node of the index.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
Qgis::PointCloudAccessType accessType() const
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
Point cloud layer specific subclass of QgsMapLayerElevationProperties.
QVector< QgsPointCloudNodeId > traverseTree(QgsPointCloudIndex &pc, QgsPointCloudNodeId n, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent)
Recursively traverses the point cloud node tree to gather visible nodes.
virtual ~QgsPointCloudLayerProfileGeneratorBase() override
std::unique_ptr< QgsPointCloudRenderer > mRenderer
virtual void visitBlock(const QgsPointCloudBlock *block, const QgsDoubleRange &zRange)=0
Visits a point cloud block and collects points within the given Z range and search geometry.
const QVector< QgsPointCloudSubIndex > mSubIndexes
void gatherPoints(QgsPointCloudIndex &pc, QgsPointCloudRequest &request, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent)
Collects points from the point cloud index within the given extent and Z range.
int visitNodesAsync(const QVector< QgsPointCloudNodeId > &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange)
Asynchronously visits point cloud nodes, loading their data and processing valid blocks.
bool collectData(QgsGeos &curve, const double mapUnitsPerPixel, const double maximumErrorPixels, const QgsDoubleRange &zRange, double &maxErrorInLayerCrs)
Collects point cloud data along a curve within the specified range and tolerance.
QgsPointCloudLayerProfileGeneratorBase(QgsPointCloudLayer *layer, const QgsProfileRequest &request)
Constructor for QgsPointCloudLayerProfileGeneratorBase.
std::unique_ptr< QgsPreparedPointCloudRendererData > mPreparedRendererData
int visitNodesSync(const QVector< QgsPointCloudNodeId > &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange)
Synchronously visits point cloud nodes and processes their data.
Qgis::ProfileGeneratorFlags flags() const override
Returns flags which reflect how the profile generator operates.
QString type() const override
Returns the unique string identifier for the results type.
QgsPointCloudLayerProfileGenerator(QgsPointCloudLayer *layer, const QgsProfileRequest &request)
Constructor for QgsPointCloudLayerProfileGenerator.
QString sourceId() const override
Returns a unique identifier representing the source of the profile.
QgsFeedback * feedback() const override
Access to feedback object of the generator (may be nullptr).
QgsAbstractProfileResults * takeResults() override
Takes results from the generator.
bool generateProfile(const QgsProfileGenerationContext &context=QgsProfileGenerationContext()) override
Generate the profile (based on data stored in the class).
QgsProfileSnapResult snapPoint(const QgsProfilePoint &point, const QgsProfileSnapContext &context) override
Snaps a point to the generated elevation profile.
void finalize(QgsFeedback *feedback)
Finalizes results – should be called after last point is added.
QgsPointSequence sampledPoints() const override
Returns a list of sampled points, with their calculated elevation as the point z value.
QgsDoubleRange zRange() const override
Returns the range of the retrieved elevation values.
QMap< double, double > distanceToHeightMap() const override
Returns the map of distance (chainage) to height.
QVector< QgsGeometry > asGeometries() const override
Returns a list of geometries representing the calculated elevation results.
void copyPropertiesFromGenerator(const QgsAbstractProfileGenerator *generator) override
Copies properties from specified generator to the results object.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context) override
Identify results visible at the specified profile point.
QString type() const override
Returns the unique string identifier for the results type.
void renderResults(QgsProfileRenderContext &context) override
Renders the results to the specified context.
QVector< QgsAbstractProfileResults::Feature > asFeatures(Qgis::ProfileExportType type, QgsFeedback *feedback=nullptr) const override
Returns a list of features representing the calculated elevation results.
Represents a map layer supporting display of point clouds.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
Represents an indexed point cloud node's position in octree.
QString toString() const
Encode node to string.
Keeps metadata for an indexed point cloud node.
QList< QgsPointCloudNodeId > children() const
Returns IDs of child nodes.
qint64 pointCount() const
Returns number of points contained in node data.
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
Encapsulates the context in which an elevation profile is to be generated.
double mapUnitsPerDistancePixel() const
Returns the number of map units per pixel in the distance dimension.
QgsDoubleRange elevationRange() const
Returns the range of elevations to include in the generation.
double convertDistanceToPixels(double size, Qgis::RenderUnit unit) const
Converts a distance size from the specified units to pixels.
QgsDoubleRange distanceRange() const
Returns the range of distances to include in the generation.
Encapsulates the context of identifying profile results.
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a point.
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when identifying a point.
QgsProject * project
Associated project.
Stores identify results generated by a QgsAbstractProfileResults object.
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.
const QTransform & worldTransform() const
Returns the transform from world coordinates to painter coordinates.
QgsDoubleRange elevationRange() const
Returns the range of elevations to include in the render.
QgsDoubleRange distanceRange() const
Returns the range of distances to include in the render.
QgsRenderContext & renderContext()
Returns a reference to the component QgsRenderContext.
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
Encapsulates the context of snapping a profile point.
double maximumPointDistanceDelta
Maximum allowed snapping delta for the distance values when snapping to a point.
double maximumPointElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a point.
double displayRatioElevationVsDistance
Display ratio of elevation vs distance units.
Encapsulates results of snapping a profile point.
QgsProfilePoint snappedPoint
Snapped point.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:120
bool overlaps(const QgsRange< T > &other) const
Returns true if this range overlaps another range.
Definition qgsrange.h:171
bool contains(const QgsRange< T > &other) const
Returns true if this range contains another range.
Definition qgsrange.h:138
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:79
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:86
A rectangle specified with double values.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
Scoped object for saving and restoring a QPainter object's state.
Triangle geometry type.
Definition qgstriangle.h:33
QgsAbstractProfileResults * takeResults() override
Takes results from the generator.
QgsFeedback * feedback() const override
Access to feedback object of the generator (may be nullptr).
bool generateProfile(const QgsProfileGenerationContext &context) override
Generate the profile (based on data stored in the class).
QString type() const override
Returns the unique string identifier for the results type.
QString sourceId() const override
Returns a unique identifier representing the source of the profile.
QgsTriangulatedPointCloudLayerProfileGenerator(QgsPointCloudLayer *layer, const QgsProfileRequest &request)
Constructor for QgsTriangulatedPointCloudLayerProfileGenerator.
QString type() const override
Returns the unique string identifier for the results type.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context) override
Identify results visible at the specified profile point.
void copyPropertiesFromGenerator(const QgsAbstractProfileGenerator *generator) override
Copies properties from specified generator to the results object.
void renderResults(QgsProfileRenderContext &context) override
Renders the results to the specified context.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition qgsgeos.h:112
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define QgsDebugError(str)
Definition qgslogger.h:59
void _GEOSQueryCallback(void *item, void *userdata)
Encapsulates information about a feature exported from the profile results.
QString layerIdentifier
Identifier for grouping output features.
QVariantMap attributes
Exported attributes.
QList< const QgsPointCloudLayerProfileResults::PointResult * > * list