QGIS API Documentation 4.1.0-Master (376402f9aeb)
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 std::unique_ptr< QgsAbstractGeometry > searchGeometryInLayerCrs;
373 searchGeometryInLayerCrs.reset( curve.buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2, nullptr, mFeedback.get() ) );
375
376 try
377 {
378 searchGeometryInLayerCrs->transform( mLayerToTargetTransform, Qgis::TransformDirection::Reverse );
379 }
380 catch ( QgsCsException & )
381 {
382 QgsDebugError( u"Error transforming profile line to layer CRS"_s );
383 return false;
384 }
385
386 mSearchGeometryInLayerCrsGeometryEngine = std::make_unique< QgsGeos >( searchGeometryInLayerCrs.get() );
388 const QgsRectangle maxSearchExtentInLayerCrs = searchGeometryInLayerCrs->boundingBox();
389
390 QVector<QgsPointCloudIndex> indexes;
391 if ( mIndex && mIndex.isValid() )
392 indexes.append( mIndex );
393
394 // Gather all relevant sub-indexes
395 for ( const QgsPointCloudSubIndex &subidx : mSubIndexes )
396 {
397 QgsPointCloudIndex index = subidx.index();
398 if ( index && index.isValid() && mSearchGeometryInLayerCrsGeometryEngine->intersects( subidx.polygonBounds().constGet() ) )
399 indexes.append( subidx.index() );
400 }
401
402 if ( indexes.empty() )
403 return false;
404
405 if ( mFeedback->isCanceled() )
406 return false;
407
408 QgsPointCloudRequest request;
413
414 if ( mRenderer )
415 {
416 mPreparedRendererData = mRenderer->prepare();
418 {
419 const QSet< QString > rendererAttributes = mPreparedRendererData->usedAttributes();
420 for ( const QString &attribute : std::as_const( rendererAttributes ) )
421 {
422 if ( attributes.indexOf( attribute ) >= 0 )
423 continue; // don't re-add attributes we are already going to fetch
424
425 const int layerIndex = mLayerAttributes.indexOf( attribute );
426 if ( layerIndex < 0 )
427 {
428 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
429 continue;
430 }
431
432 attributes.push_back( mLayerAttributes.at( layerIndex ) );
433 }
434 }
435 }
436 else
437 {
438 mPreparedRendererData.reset();
439 }
440
441 request.setAttributes( attributes );
442
444 extentTransform.setBallparkTransformsAreAppropriate( true );
445
446
447 for ( QgsPointCloudIndex pc : std::as_const( indexes ) )
448 {
449 const QgsPointCloudNode root = pc.getNode( pc.root() );
450 const QgsRectangle rootNodeExtentLayerCoords = root.bounds().toRectangle();
451 QgsRectangle rootNodeExtentInCurveCrs;
452 try
453 {
454 rootNodeExtentInCurveCrs = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
455 }
456 catch ( QgsCsException & )
457 {
458 QgsDebugError( u"Could not transform node extent to curve CRS"_s );
459 rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords;
460 }
461
462 const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.width() / pc.span(); // in curve coords
463 if ( rootErrorInMapCoordinates < 0.0 )
464 {
465 QgsDebugError( u"Invalid root node error"_s );
466 return false;
467 }
468
469 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
470 gatherPoints( pc, request, maximumErrorPixels, rootErrorPixels, zRange, maxSearchExtentInLayerCrs );
471
472 if ( mFeedback->isCanceled() )
473 return false;
474
475 const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.width() / pc.span();
476 const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel;
477
478 maxErrorInLayerCoordinates = std::max( maxErrorInLayerCoordinates, maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates );
479
480 if ( mFeedback->isCanceled() )
481 return false;
482 }
483
484 return true;
485}
486
488 QgsPointCloudIndex &pc, QgsPointCloudNodeId n, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent
489)
490{
491 QVector<QgsPointCloudNodeId> nodes;
492
493 if ( mFeedback->isCanceled() )
494 {
495 return nodes;
496 }
497
498 QgsPointCloudNode node = pc.getNode( n );
499 QgsBox3D nodeBounds = node.bounds();
500 const QgsDoubleRange nodeZRange( nodeBounds.zMinimum(), nodeBounds.zMaximum() );
501 if ( !zRange.isInfinite() && !zRange.overlaps( nodeZRange ) )
502 return nodes;
503
504 if ( !searchExtent.intersects( nodeBounds.toRectangle() ) )
505 return nodes;
506
507 const QgsGeometry nodeMapGeometry = QgsGeometry::fromRect( nodeBounds.toRectangle() );
508 if ( !mSearchGeometryInLayerCrsGeometryEngine->intersects( nodeMapGeometry.constGet() ) )
509 return nodes;
510
511 if ( node.pointCount() > 0 )
512 nodes.append( n );
513
514 double childrenErrorPixels = nodeErrorPixels / 2.0;
515 if ( childrenErrorPixels < maxErrorPixels )
516 return nodes;
517
518 for ( QgsPointCloudNodeId nn : node.children() )
519 {
520 nodes += traverseTree( pc, nn, maxErrorPixels, childrenErrorPixels, zRange, searchExtent );
521 }
522
523 return nodes;
524}
525
526int QgsPointCloudLayerProfileGeneratorBase::visitNodesSync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
527{
528 int nodesDrawn = 0;
529 for ( QgsPointCloudNodeId n : nodes )
530 {
531 if ( mFeedback->isCanceled() )
532 break;
533
534 std::unique_ptr<QgsPointCloudBlock> block( pc.nodeData( n, request ) );
535
536 if ( !block )
537 continue;
538
539 visitBlock( block.get(), zRange );
540
541 ++nodesDrawn;
542 }
543 return nodesDrawn;
544}
545
546int QgsPointCloudLayerProfileGeneratorBase::visitNodesAsync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
547{
548 if ( nodes.isEmpty() )
549 return 0;
550
551 int nodesDrawn = 0;
552
553 // see notes about this logic in QgsPointCloudLayerRenderer::renderNodesAsync
554
555 // Async loading of nodes
556 QVector<QgsPointCloudBlockRequest *> blockRequests;
557 QEventLoop loop;
558 QObject::connect( mFeedback.get(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
559
560 for ( int i = 0; i < nodes.size(); ++i )
561 {
562 QgsPointCloudNodeId n = nodes[i];
563 const QString nStr = n.toString();
564 QgsPointCloudBlockRequest *blockRequest = pc.asyncNodeData( n, request );
565 blockRequests.append( blockRequest );
566 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop, [this, &nodesDrawn, &loop, &blockRequests, &zRange, nStr, blockRequest]() {
567 blockRequests.removeOne( blockRequest );
568
569 // If all blocks are loaded, exit the event loop
570 if ( blockRequests.isEmpty() )
571 loop.exit();
572
573 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
574
575 blockRequest->deleteLater();
576
577 if ( mFeedback->isCanceled() )
578 {
579 return;
580 }
581
582 if ( !block )
583 {
584 QgsDebugError( u"Unable to load node %1, error: %2"_s.arg( nStr, blockRequest->errorStr() ) );
585 return;
586 }
587
588 visitBlock( block.get(), zRange );
589 ++nodesDrawn;
590 } );
591 }
592
593 // Wait for all point cloud nodes to finish loading
594 if ( !blockRequests.isEmpty() )
595 loop.exec();
596
597 // Generation may have got canceled and the event loop exited before finished()
598 // was called for all blocks, so let's clean up anything that is left
599 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
600 {
601 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
602 block.reset();
603 blockRequest->deleteLater();
604 }
605
606 return nodesDrawn;
607}
608
610 QgsPointCloudIndex &pc, QgsPointCloudRequest &request, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent
611)
612{
613 const QVector<QgsPointCloudNodeId> nodes = traverseTree( pc, pc.root(), maxErrorPixels, nodeErrorPixels, zRange, searchExtent );
614
615 if ( nodes.empty() )
616 {
617 return;
618 }
619
620 switch ( pc.accessType() )
621 {
623 {
624 visitNodesSync( nodes, pc, request, zRange );
625 break;
626 }
628 {
629 visitNodesAsync( nodes, pc, request, zRange );
630 break;
631 }
632 }
633}
634
635//
636// QgsPointCloudLayerProfileGenerator
637//
638
641 , mPointSize( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSize() )
642 , mPointSizeUnit( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSizeUnit() )
643 , mPointSymbol( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointSymbol() )
644 , mPointColor( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->pointColor() )
645 , mOpacityByDistanceEffect( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->applyOpacityByDistanceEffect() )
646{}
647
649{
650 return mId;
651}
652
654{
655 return u"pointcloud"_s;
656}
657
662
664
666{
667 mGatheredPoints.clear();
668
669 const double startDistanceOffset = std::max( !context.distanceRange().isInfinite() ? context.distanceRange().lower() : 0, 0.0 );
670 const double endDistance = context.distanceRange().upper();
671
672 std::unique_ptr< QgsCurve > trimmedCurve;
673 QgsCurve *sourceCurve = nullptr;
674 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
675 {
676 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
677 sourceCurve = trimmedCurve.get();
678 }
679 else
680 {
681 sourceCurve = mProfileCurve.get();
682 }
683
684 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
685 if ( maximumErrorPixels < 0.0 )
686 {
687 QgsDebugError( u"Invalid maximum error in pixels"_s );
688 return false;
689 }
690
691 const double toleranceInPixels = context.convertDistanceToPixels( mTolerance, Qgis::RenderUnit::MapUnits );
692 // ensure that the maximum error is compatible with the tolerance size -- otherwise if the tolerance size
693 // is much smaller than the maximum error, we don't dig deep enough into the point cloud nodes to find
694 // points which are inside the tolerance.
695 // "4" is a magic number here, based purely on what "looks good" in the profile results!
696 if ( toleranceInPixels / 4 < maximumErrorPixels )
697 maximumErrorPixels = toleranceInPixels / 4;
698
699 double maxErrorInLayerCrs;
700
701 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
702 if ( mapUnitsPerPixel < 0.0 )
703 {
704 QgsDebugError( u"Invalid map units per pixel ratio"_s );
705 return false;
706 }
707
708 QgsGeos originalCurveGeos( sourceCurve );
709 originalCurveGeos.prepareGeometry();
710
711 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, context.elevationRange(), maxErrorInLayerCrs ) || mGatheredPoints.empty() )
712 {
713 mResults = nullptr;
714 return false;
715 }
716
717
718 mResults = std::make_unique< QgsPointCloudLayerProfileResults >();
719 mResults->copyPropertiesFromGenerator( this );
720 mResults->mMaxErrorInLayerCoordinates = maxErrorInLayerCrs;
721
722 // convert x/y values back to distance/height values
723 QString lastError;
724 const QgsPointCloudLayerProfileResults::PointResult *pointData = mGatheredPoints.constData();
725 const int size = mGatheredPoints.size();
726 mResults->results.resize( size );
727 QgsPointCloudLayerProfileResults::PointResult *destData = mResults->results.data();
728 for ( int i = 0; i < size; ++i, ++pointData, ++destData )
729 {
730 if ( mFeedback->isCanceled() )
731 return false;
732
733 *destData = *pointData;
734 destData->distanceAlongCurve = startDistanceOffset + originalCurveGeos.lineLocatePoint( destData->x, destData->y, &lastError );
735 if ( mOpacityByDistanceEffect ) // don't calculate this if we don't need it
736 destData->distanceFromCurve = originalCurveGeos.distance( destData->x, destData->y );
737
738 mResults->minZ = std::min( destData->z, mResults->minZ );
739 mResults->maxZ = std::max( destData->z, mResults->maxZ );
740 }
741 mResults->finalize( mFeedback.get() );
742
743 return true;
744}
745
750
755
756void QgsPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
757{
758 const char *ptr = block->data();
759 int count = block->pointCount();
760
761 const QgsPointCloudAttributeCollection request = block->attributes();
762
763 const std::size_t recordSize = request.pointRecordSize();
764
765 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
766 int xOffset = 0, yOffset = 0, zOffset = 0;
767 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
768 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
769 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
770
771 bool useRenderer = false;
773 {
774 useRenderer = mPreparedRendererData->prepareBlock( block );
775 }
776
777 QColor color;
778 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
779 for ( int i = 0; i < count; ++i )
780 {
781 if ( mFeedback->isCanceled() )
782 {
783 break;
784 }
785
786 QgsPointCloudLayerProfileResults::PointResult res;
787 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), res.x, res.y, res.z );
788
789 res.z = res.z * mZScale + mZOffset;
790 if ( !zRange.contains( res.z ) )
791 continue;
792
793 if ( useRenderer )
794 {
795 color = mPreparedRendererData->pointColor( block, i, res.z );
796 if ( !color.isValid() )
797 continue;
798
799 res.color = color.rgba();
800 }
801 else
802 {
803 res.color = mPointColor.rgba();
804 }
805
806 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x, res.y ) )
807 {
808 if ( reproject )
809 {
810 try
811 {
812 mLayerToTargetTransform.transformInPlace( res.x, res.y, res.z );
813 }
814 catch ( QgsCsException & )
815 {
816 continue;
817 }
818 }
819
820 mGatheredPoints.append( res );
821 }
822 }
823}
824
827{
828 mSymbology = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileSymbology();
829 mElevationLimit = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->elevationLimit();
830
831 mLineSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileLineSymbol()->clone() );
832 mFillSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileFillSymbol()->clone() );
833}
834
835void QgsTriangulatedPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
836{
837 const char *ptr = block->data();
838 int count = block->pointCount();
839
840 const QgsPointCloudAttributeCollection request = block->attributes();
841
842 const std::size_t recordSize = request.pointRecordSize();
843
844 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
845 int xOffset = 0, yOffset = 0, zOffset = 0;
846 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
847 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
848 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
849
850 bool useRenderer = false;
852 {
853 useRenderer = mPreparedRendererData->prepareBlock( block );
854 }
855
856 QColor color;
857 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
858 for ( int i = 0; i < count; ++i )
859 {
860 if ( mFeedback->isCanceled() )
861 {
862 break;
863 }
864
865 double x, y, z;
866 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
867 QgsPoint res( x, y, z * mZScale + mZOffset );
868 if ( !zRange.contains( res.z() ) )
869 continue;
870
871 if ( useRenderer )
872 {
873 color = mPreparedRendererData->pointColor( block, i, res.z() );
874 if ( !color.isValid() )
875 continue;
876 }
877
878 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x(), res.y() ) )
879 {
880 if ( reproject )
881 {
882 try
883 {
884 double x = res.x(), y = res.y(), z = res.z();
885 mLayerToTargetTransform.transformInPlace( x, y, z );
886 res.setX( x );
887 res.setY( y );
888 res.setZ( z );
889 }
890 catch ( QgsCsException & )
891 {
892 continue;
893 }
894 }
895
896 mGatheredPoints.append( res );
897 }
898 }
899}
900
905
907{
908 return u"triangulatedpointcloud"_s;
909}
910
912
914{
915 mGatheredPoints.clear();
916
917 QgsCurve *sourceCurve = mProfileCurve.get();
918
919 // we need to transform the profile curve and max search extent to the layer's CRS
920 QgsGeos originalCurveGeos( sourceCurve );
921 originalCurveGeos.prepareGeometry();
922
923 if ( mFeedback->isCanceled() )
924 return false;
925
926 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
927 if ( maximumErrorPixels < 0.0 )
928 {
929 QgsDebugError( u"Invalid maximum error in pixels"_s );
930 return false;
931 }
932
933 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
934 if ( mapUnitsPerPixel < 0.0 )
935 {
936 QgsDebugError( u"Invalid map units per pixel ratio"_s );
937 return false;
938 }
939
940 double maxErrorInLayerCrs;
941 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, QgsDoubleRange(), maxErrorInLayerCrs ) )
942 return false;
943
944 if ( mGatheredPoints.size() < 3 )
945 {
946 mResults = nullptr;
947 return false;
948 }
949
950 mResults = std::make_unique< QgsTriangulatedPointCloudLayerProfileResults >();
951 mResults->copyPropertiesFromGenerator( this );
952
953 std::unique_ptr<delaunator::Delaunator> triangulator;
954 std::vector<double> vertices;
955 std::vector<double> zValues;
956
957 vertices.reserve( mGatheredPoints.size() * 2 );
958 zValues.reserve( mGatheredPoints.size() );
959
960 for ( QgsPoint point : mGatheredPoints )
961 {
962 vertices.push_back( point.x() );
963 vertices.push_back( point.y() );
964 zValues.push_back( point.z() );
965 }
966
967 triangulator.reset( new delaunator::Delaunator( vertices ) );
968 const std::vector<size_t> &triangleIndexes = triangulator->triangles;
969
970 QgsPointSequence profilePoints;
971 QString lastError;
972 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
973 {
974 const size_t idx0 = triangleIndexes[i];
975 const size_t idx1 = triangleIndexes[i + 1];
976 const size_t idx2 = triangleIndexes[i + 2];
977
978 const QgsPoint pt0( vertices[2 * idx0], vertices[2 * idx0 + 1], zValues[idx0] );
979 const QgsPoint pt1( vertices[2 * idx1], vertices[2 * idx1 + 1], zValues[idx1] );
980 const QgsPoint pt2( vertices[2 * idx2], vertices[2 * idx2 + 1], zValues[idx2] );
981
982 const QgsTriangle triangle( pt0, pt1, pt2 );
983
984 QgsGeos geosPoly( &triangle );
985 geosPoly.prepareGeometry();
986
987 std::unique_ptr<QgsAbstractGeometry> intersectingGeom( geosPoly.intersection( sourceCurve, nullptr, QgsGeometryParameters(), mFeedback.get() ) );
988 if ( !intersectingGeom )
989 continue;
990
991 // probably not needed, but we may end up with something else, better to check
992 if ( QgsWkbTypes::flatType( intersectingGeom->wkbType() ) != Qgis::WkbType::LineString )
993 continue;
994
995 const QgsLineString *linestring = qgsgeometry_cast<const QgsLineString *>( intersectingGeom.get() );
996 for ( const QgsPoint &p : { linestring->startPoint(), linestring->endPoint() } )
997 {
998 const double z = QgsGeometryUtils::interpolateZ( pt0, pt1, pt2, p.x(), p.y() );
999 if ( std::isnan( z ) )
1000 continue;
1001
1002 const double distanceAlong = originalCurveGeos.lineLocatePoint( p, &lastError );
1003 if ( std::isnan( distanceAlong ) )
1004 continue;
1005
1006 QgsPoint profilePoint( p.x(), p.y(), z, distanceAlong );
1007 profilePoints.append( profilePoint );
1008 }
1009
1010 if ( mFeedback->isCanceled() )
1011 return false;
1012 }
1013
1014 std::sort( profilePoints.begin(), profilePoints.end(), []( const QgsPoint &a, const QgsPoint &b ) { return a.m() < b.m(); } );
1015
1016 if ( mResults )
1017 {
1018 for ( const QgsPoint &pp : std::as_const( profilePoints ) )
1019 {
1020 const double z = pp.z();
1021 const double distanceAlong = pp.m();
1022
1023 mResults->minZ = std::min( z, mResults->minZ );
1024 mResults->maxZ = std::max( z, mResults->maxZ );
1025 mResults->mDistanceToHeightMap.insert( distanceAlong, z );
1026 mResults->mRawPoints.append( pp );
1027 }
1028 }
1029
1030 return true;
1031}
1032
1037
1042
1044{
1045 return u"triangulatedpointcloud"_s;
1046}
1047
1048QVector<QgsProfileIdentifyResults> QgsTriangulatedPointCloudLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
1049{
1050 const QVector<QgsProfileIdentifyResults> noLayerResults = QgsAbstractProfileSurfaceResults::identify( point, context );
1051
1052 // we have to make a new list, with the correct layer reference set
1053 QVector<QgsProfileIdentifyResults> res;
1054 res.reserve( noLayerResults.size() );
1055 for ( const QgsProfileIdentifyResults &result : noLayerResults )
1056 {
1057 res.append( QgsProfileIdentifyResults( mLayer, result.results() ) );
1058 }
1059 return res;
1060}
1061
1063{
1064 const QgsTriangulatedPointCloudLayerProfileGenerator *pcGenerator = qgis::down_cast< const QgsTriangulatedPointCloudLayerProfileGenerator * >( generator );
1065
1066 mLayer = pcGenerator->mLayer;
1067 mLayerId = pcGenerator->mId;
1068 mCurveCrs = pcGenerator->mTargetCrs;
1069 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
1070 mTolerance = pcGenerator->mTolerance;
1071
1072 mLineSymbol.reset( pcGenerator->mLineSymbol->clone() );
1073 mFillSymbol.reset( pcGenerator->mFillSymbol->clone() );
1074 symbology = pcGenerator->mSymbology;
1075 mElevationLimit = pcGenerator->mElevationLimit;
1076
1077 mZOffset = pcGenerator->mZOffset;
1078 mZScale = pcGenerator->mZScale;
1079}
1080
@ RespectsMaximumErrorMapUnit
Generated profile respects the QgsProfileGenerationContext::maximumErrorMapUnits() property.
Definition qgis.h:4488
@ RespectsDistanceRange
Generated profile respects the QgsProfileGenerationContext::distanceRange() property.
Definition qgis.h:4489
@ Circle
Renders points as circles.
Definition qgis.h:4517
@ Square
Renders points as squares.
Definition qgis.h:4516
QFlags< ProfileGeneratorFlag > ProfileGeneratorFlags
Definition qgis.h:4493
@ Round
Use rounded joins.
Definition qgis.h:2243
@ MapUnits
Map units.
Definition qgis.h:5498
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2231
@ Local
Local means the source is a local file on the machine.
Definition qgis.h:6589
@ Remote
Remote means it's loaded through a protocol like HTTP.
Definition qgis.h:6590
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4502
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4504
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4503
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4505
@ LineString
LineString.
Definition qgis.h:297
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2833
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.
Encapsulates parameters under which a geometry operation is performed.
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:175
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:587
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the intersection of this and geom.
Definition qgsgeos.cpp:320
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
Definition qgsgeos.cpp:3283
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
Definition qgsgeos.cpp:2106
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition qgsgeos.cpp:292
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.
std::unique_ptr< QgsPointCloudBlock > nodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns node data block.
QgsPointCloudNode getNode(QgsPointCloudNodeId id) const
Returns object for a given node.
bool isValid() const
Returns whether index is loaded and valid.
QgsPointCloudNodeId root() const
Returns root node of the index.
QgsPointCloudBlockRequest * asyncNodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
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:121
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:148
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