QGIS API Documentation 3.99.0-Master (8e76e220402)
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 =
158 {
159 { u"distance"_s, point.distanceAlongCurve },
160 { u"elevation"_s, point.z }
161 };
162 f.geometry = QgsGeometry( std::make_unique< QgsPoint >( point.x, point.y, point.z ) );
163 res << f;
164 }
165 break;
166 }
167 }
168
169 return res;
170}
171
176
178{
179 QPainter *painter = context.renderContext().painter();
180 if ( !painter )
181 return;
182
183 const QgsScopedQPainterState painterState( painter );
184
185 painter->setBrush( Qt::NoBrush );
186 painter->setPen( Qt::NoPen );
187
188 switch ( pointSymbol )
189 {
191 // for square point we always disable antialiasing -- it's not critical here and we benefit from the performance boost disabling it gives
192 context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );
193 break;
194
196 break;
197 }
198
199 const double minDistance = context.distanceRange().lower();
200 const double maxDistance = context.distanceRange().upper();
201 const double minZ = context.elevationRange().lower();
202 const double maxZ = context.elevationRange().upper();
203
204 const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
205 QPainterPath clipPath;
206 clipPath.addPolygon( context.worldTransform().map( visibleRegion ) );
207 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
208
209 const double penWidth = context.renderContext().convertToPainterUnits( pointSize, pointSizeUnit );
210
211 for ( const PointResult &point : std::as_const( results ) )
212 {
213 QPointF p = context.worldTransform().map( QPointF( point.distanceAlongCurve, point.z ) );
214 QColor color = respectLayerColors ? point.color : pointColor;
216 color.setAlphaF( color.alphaF() * ( 1.0 - std::pow( point.distanceFromCurve / tolerance, 0.5 ) ) );
217
218 switch ( pointSymbol )
219 {
221 painter->fillRect( QRectF( p.x() - penWidth * 0.5,
222 p.y() - penWidth * 0.5,
223 penWidth, penWidth ), color );
224 break;
225
227 painter->setBrush( QBrush( color ) );
228 painter->setPen( Qt::NoPen );
229 painter->drawEllipse( QRectF( p.x() - penWidth * 0.5,
230 p.y() - penWidth * 0.5,
231 penWidth, penWidth ) );
232 break;
233 }
234 }
235}
236
238{
239 QList< const QgsPointCloudLayerProfileResults::PointResult * > *list;
240};
241void _GEOSQueryCallback( void *item, void *userdata )
242{
243 reinterpret_cast<_GEOSQueryCallbackData *>( userdata )->list->append( reinterpret_cast<const QgsPointCloudLayerProfileResults::PointResult *>( item ) );
244}
245
247{
249
250 GEOSContextHandle_t geosctxt = QgsGeosContext::get();
251
252 const double minDistance = point.distance() - context.maximumPointDistanceDelta;
253 const double maxDistance = point.distance() + context.maximumPointDistanceDelta;
254 const double minElevation = point.elevation() - context.maximumPointElevationDelta;
255 const double maxElevation = point.elevation() + context.maximumPointElevationDelta;
256
257 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
258 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, minDistance, minElevation );
259 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, maxDistance, maxElevation );
260 geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
261
262 QList<const PointResult *> items;
263 struct _GEOSQueryCallbackData callbackData;
264 callbackData.list = &items;
265 GEOSSTRtree_query_r( geosctxt, mPointIndex, searchDiagonal.get(), _GEOSQueryCallback, &callbackData );
266 if ( items.empty() )
267 return result;
268
269 double bestMatchDistance = std::numeric_limits< double >::max();
270 const PointResult *bestMatch = nullptr;
271 for ( const PointResult *candidate : std::as_const( items ) )
272 {
273 const double distance = std::sqrt( std::pow( candidate->distanceAlongCurve - point.distance(), 2 )
274 + std::pow( ( candidate->z - point.elevation() ) / context.displayRatioElevationVsDistance, 2 ) );
275 if ( distance < bestMatchDistance )
276 {
277 bestMatchDistance = distance;
278 bestMatch = candidate;
279 }
280 }
281 if ( !bestMatch )
282 return result;
283
284 result.snappedPoint = QgsProfilePoint( bestMatch->distanceAlongCurve, bestMatch->z );
285 return result;
286}
287
288QVector<QgsProfileIdentifyResults> QgsPointCloudLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
289{
290 return identify( QgsDoubleRange( point.distance() - context.maximumPointDistanceDelta, point.distance() + context.maximumPointDistanceDelta ),
291 QgsDoubleRange( point.elevation() - context.maximumPointElevationDelta, point.elevation() + context.maximumPointElevationDelta ), context );
292}
293
294QVector<QgsProfileIdentifyResults> QgsPointCloudLayerProfileResults::identify( const QgsDoubleRange &distanceRange, const QgsDoubleRange &elevationRange, const QgsProfileIdentifyContext &context )
295{
296 if ( !mLayer )
297 return {};
298
299 std::unique_ptr< QgsCurve > substring( mProfileCurve->curveSubstring( distanceRange.lower(), distanceRange.upper() ) );
300 QgsGeos substringGeos( substring.get() );
301 std::unique_ptr< QgsAbstractGeometry > searchGeometry( substringGeos.buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
302 if ( !searchGeometry )
303 return {};
304
305 const QgsCoordinateTransform curveToLayerTransform = QgsCoordinateTransform( mCurveCrs, mLayer->crs(), context.project ? context.project->transformContext() : QgsCoordinateTransformContext() );
306 try
307 {
308 searchGeometry->transform( curveToLayerTransform );
309 }
310 catch ( QgsCsException & )
311 {
312 return {};
313 }
314
315 // we have to adjust the elevation range to "undo" the z offset/z scale before we can hand to the provider
316 const QgsDoubleRange providerElevationRange( ( elevationRange.lower() - mZOffset ) / mZScale, ( elevationRange.upper() - mZOffset ) / mZScale );
317
318 const QgsGeometry pointCloudSearchGeometry( std::move( searchGeometry ) );
319 const QVector<QVariantMap> pointAttributes = mLayer->dataProvider()->identify( mMaxErrorInLayerCoordinates, pointCloudSearchGeometry, providerElevationRange );
320 if ( pointAttributes.empty() )
321 return {};
322
323 return { QgsProfileIdentifyResults( mLayer, pointAttributes )};
324}
325
327{
328 const QgsPointCloudLayerProfileGenerator *pcGenerator = qgis::down_cast< const QgsPointCloudLayerProfileGenerator *>( generator );
329 tolerance = pcGenerator->mTolerance;
330 pointSize = pcGenerator->mPointSize;
331 pointSizeUnit = pcGenerator->mPointSizeUnit;
332 pointSymbol = pcGenerator->mPointSymbol;
333 pointColor = pcGenerator->mPointColor;
334 respectLayerColors = static_cast< bool >( pcGenerator->mRenderer );
335 opacityByDistanceEffect = pcGenerator->mOpacityByDistanceEffect;
336
337 mLayer = pcGenerator->mLayer;
338 mLayerId = pcGenerator->mId;
339 mCurveCrs = pcGenerator->mTargetCrs;
340 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
341 mTolerance = pcGenerator->mTolerance;
342
343 mZOffset = pcGenerator->mZOffset;
344 mZScale = pcGenerator->mZScale;
345}
346
347//
348// QgsPointCloudLayerProfileGeneratorBase
349//
350
352 : mLayer( layer )
353 , mIndex( layer->index() )
354 , mSubIndexes( layer->dataProvider()->subIndexes() )
355 , mLayerAttributes( layer->attributes() )
356 , mRenderer( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->respectLayerColors() && mLayer->renderer() ? mLayer->renderer()->clone() : nullptr )
357 , mMaximumScreenError( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->maximumScreenError() )
358 , mMaximumScreenErrorUnit( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->maximumScreenErrorUnit() )
359 , mId( layer->id() )
360 , mFeedback( std::make_unique< QgsFeedback >() )
361 , mTolerance( request.tolerance() )
362 , mSourceCrs( layer->crs3D() )
363 , mTargetCrs( request.crs() )
364 , mTransformContext( request.transformContext() )
365 , mStepDistance( request.stepDistance() )
366 , mZOffset( layer->elevationProperties()->zOffset() )
367 , mZScale( layer->elevationProperties()->zScale() )
368 , mLayerToTargetTransform( layer->crs3D(), request.crs(), request.transformContext() )
369 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
370{
371}
372
374
375bool QgsPointCloudLayerProfileGeneratorBase::collectData( QgsGeos &curve, const double mapUnitsPerPixel, const double maximumErrorPixels, const QgsDoubleRange &zRange, double &maxErrorInLayerCoordinates )
376{
377 maxErrorInLayerCoordinates = 0;
378
379 QVector<QgsPointCloudIndex> indexes;
380 if ( mIndex && mIndex.isValid() )
381 indexes.append( mIndex );
382
383 // Gather all relevant sub-indexes
384 const QgsRectangle profileCurveBbox = mProfileCurve->boundingBox();
385 for ( const QgsPointCloudSubIndex &subidx : mSubIndexes )
386 {
387 QgsPointCloudIndex index = subidx.index();
388 if ( index && index.isValid() && subidx.polygonBounds().intersects( profileCurveBbox ) )
389 indexes.append( subidx.index() );
390 }
391
392 if ( indexes.empty() )
393 return false;
394
395 std::unique_ptr< QgsAbstractGeometry > searchGeometryInLayerCrs;
396 searchGeometryInLayerCrs.reset( curve.buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
398
399 try
400 {
401 searchGeometryInLayerCrs->transform( mLayerToTargetTransform, Qgis::TransformDirection::Reverse );
402 }
403 catch ( QgsCsException & )
404 {
405 QgsDebugError( u"Error transforming profile line to layer CRS"_s );
406 return false;
407 }
408
409 if ( mFeedback->isCanceled() )
410 return false;
411
412 mSearchGeometryInLayerCrsGeometryEngine = std::make_unique< QgsGeos >( searchGeometryInLayerCrs.get() );
414 const QgsRectangle maxSearchExtentInLayerCrs = searchGeometryInLayerCrs->boundingBox();
415
416 QgsPointCloudRequest request;
421
422 if ( mRenderer )
423 {
424 mPreparedRendererData = mRenderer->prepare();
426 {
427 const QSet< QString > rendererAttributes = mPreparedRendererData->usedAttributes();
428 for ( const QString &attribute : std::as_const( rendererAttributes ) )
429 {
430 if ( attributes.indexOf( attribute ) >= 0 )
431 continue; // don't re-add attributes we are already going to fetch
432
433 const int layerIndex = mLayerAttributes.indexOf( attribute );
434 if ( layerIndex < 0 )
435 {
436 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
437 continue;
438 }
439
440 attributes.push_back( mLayerAttributes.at( layerIndex ) );
441 }
442 }
443 }
444 else
445 {
446 mPreparedRendererData.reset();
447 }
448
449 request.setAttributes( attributes );
450
452 extentTransform.setBallparkTransformsAreAppropriate( true );
453
454
455 for ( QgsPointCloudIndex pc : std::as_const( indexes ) )
456 {
457 const QgsPointCloudNode root = pc.getNode( pc.root() );
458 const QgsRectangle rootNodeExtentLayerCoords = root.bounds().toRectangle();
459 QgsRectangle rootNodeExtentInCurveCrs;
460 try
461 {
462 rootNodeExtentInCurveCrs = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
463 }
464 catch ( QgsCsException & )
465 {
466 QgsDebugError( u"Could not transform node extent to curve CRS"_s );
467 rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords;
468 }
469
470 const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.width() / pc.span(); // in curve coords
471 if ( rootErrorInMapCoordinates < 0.0 )
472 {
473 QgsDebugError( u"Invalid root node error"_s );
474 return false;
475 }
476
477 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
478 gatherPoints( pc, request, maximumErrorPixels, rootErrorPixels, zRange, maxSearchExtentInLayerCrs );
479
480 if ( mFeedback->isCanceled() )
481 return false;
482
483 const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.width() / pc.span();
484 const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel;
485
486 maxErrorInLayerCoordinates = std::max( maxErrorInLayerCoordinates, maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates );
487
488 if ( mFeedback->isCanceled() )
489 return false;
490 }
491
492 return true;
493}
494
495QVector<QgsPointCloudNodeId> QgsPointCloudLayerProfileGeneratorBase::traverseTree( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent )
496{
497 QVector<QgsPointCloudNodeId> nodes;
498
499 if ( mFeedback->isCanceled() )
500 {
501 return nodes;
502 }
503
504 QgsPointCloudNode node = pc.getNode( n );
505 QgsBox3D nodeBounds = node.bounds();
506 const QgsDoubleRange nodeZRange( nodeBounds.zMinimum(), nodeBounds.zMaximum() );
507 if ( !zRange.isInfinite() && !zRange.overlaps( nodeZRange ) )
508 return nodes;
509
510 if ( !searchExtent.intersects( nodeBounds.toRectangle() ) )
511 return nodes;
512
513 const QgsGeometry nodeMapGeometry = QgsGeometry::fromRect( nodeBounds.toRectangle() );
514 if ( !mSearchGeometryInLayerCrsGeometryEngine->intersects( nodeMapGeometry.constGet() ) )
515 return nodes;
516
517 if ( node.pointCount() > 0 )
518 nodes.append( n );
519
520 double childrenErrorPixels = nodeErrorPixels / 2.0;
521 if ( childrenErrorPixels < maxErrorPixels )
522 return nodes;
523
524 for ( const QgsPointCloudNodeId &nn : node.children() )
525 {
526 nodes += traverseTree( pc, nn, maxErrorPixels, childrenErrorPixels, zRange, searchExtent );
527 }
528
529 return nodes;
530}
531
532int QgsPointCloudLayerProfileGeneratorBase::visitNodesSync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
533{
534 int nodesDrawn = 0;
535 for ( const QgsPointCloudNodeId &n : nodes )
536 {
537 if ( mFeedback->isCanceled() )
538 break;
539
540 std::unique_ptr<QgsPointCloudBlock> block( pc.nodeData( n, request ) );
541
542 if ( !block )
543 continue;
544
545 visitBlock( block.get(), zRange );
546
547 ++nodesDrawn;
548 }
549 return nodesDrawn;
550}
551
552int QgsPointCloudLayerProfileGeneratorBase::visitNodesAsync( const QVector<QgsPointCloudNodeId> &nodes, QgsPointCloudIndex &pc, QgsPointCloudRequest &request, const QgsDoubleRange &zRange )
553{
554 if ( nodes.isEmpty() )
555 return 0;
556
557 int nodesDrawn = 0;
558
559 // see notes about this logic in QgsPointCloudLayerRenderer::renderNodesAsync
560
561 // Async loading of nodes
562 QVector<QgsPointCloudBlockRequest *> blockRequests;
563 QEventLoop loop;
564 QObject::connect( mFeedback.get(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
565
566 for ( int i = 0; i < nodes.size(); ++i )
567 {
568 const QgsPointCloudNodeId &n = nodes[i];
569 const QString nStr = n.toString();
570 QgsPointCloudBlockRequest *blockRequest = pc.asyncNodeData( n, request );
571 blockRequests.append( blockRequest );
572 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop,
573 [ this, &nodesDrawn, &loop, &blockRequests, &zRange, nStr, blockRequest ]()
574 {
575 blockRequests.removeOne( blockRequest );
576
577 // If all blocks are loaded, exit the event loop
578 if ( blockRequests.isEmpty() )
579 loop.exit();
580
581 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
582
583 blockRequest->deleteLater();
584
585 if ( mFeedback->isCanceled() )
586 {
587 return;
588 }
589
590 if ( !block )
591 {
592 QgsDebugError( u"Unable to load node %1, error: %2"_s.arg( nStr, blockRequest->errorStr() ) );
593 return;
594 }
595
596 visitBlock( block.get(), zRange );
597 ++nodesDrawn;
598 } );
599 }
600
601 // Wait for all point cloud nodes to finish loading
602 if ( !blockRequests.isEmpty() )
603 loop.exec();
604
605 // Generation may have got canceled and the event loop exited before finished()
606 // was called for all blocks, so let's clean up anything that is left
607 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
608 {
609 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
610 block.reset();
611 blockRequest->deleteLater();
612 }
613
614 return nodesDrawn;
615}
616
617void QgsPointCloudLayerProfileGeneratorBase::gatherPoints( QgsPointCloudIndex &pc, QgsPointCloudRequest &request, double maxErrorPixels, double nodeErrorPixels, const QgsDoubleRange &zRange, const QgsRectangle &searchExtent )
618{
619 const QVector<QgsPointCloudNodeId> nodes = traverseTree( pc, pc.root(), maxErrorPixels, nodeErrorPixels, zRange, searchExtent );
620
621 if ( nodes.empty() )
622 {
623 return;
624 }
625
626 switch ( pc.accessType() )
627 {
629 {
630 visitNodesSync( nodes, pc, request, zRange );
631 break;
632 }
634 {
635 visitNodesAsync( nodes, pc, request, zRange );
636 break;
637 }
638 }
639}
640
641//
642// QgsPointCloudLayerProfileGenerator
643//
644
647 , mPointSize( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->pointSize() )
648 , mPointSizeUnit( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->pointSizeUnit() )
649 , mPointSymbol( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->pointSymbol() )
650 , mPointColor( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->pointColor() )
651 , mOpacityByDistanceEffect( qgis::down_cast< QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->applyOpacityByDistanceEffect() )
652{
653}
654
656{
657 return mId;
658}
659
661{
662 return u"pointcloud"_s;
663}
664
669
671
673{
674 mGatheredPoints.clear();
675
676 const double startDistanceOffset = std::max( !context.distanceRange().isInfinite() ? context.distanceRange().lower() : 0, 0.0 );
677 const double endDistance = context.distanceRange().upper();
678
679 std::unique_ptr< QgsCurve > trimmedCurve;
680 QgsCurve *sourceCurve = nullptr;
681 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
682 {
683 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
684 sourceCurve = trimmedCurve.get();
685 }
686 else
687 {
688 sourceCurve = mProfileCurve.get();
689 }
690
691 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
692 if ( maximumErrorPixels < 0.0 )
693 {
694 QgsDebugError( u"Invalid maximum error in pixels"_s );
695 return false;
696 }
697
698 const double toleranceInPixels = context.convertDistanceToPixels( mTolerance, Qgis::RenderUnit::MapUnits );
699 // ensure that the maximum error is compatible with the tolerance size -- otherwise if the tolerance size
700 // is much smaller than the maximum error, we don't dig deep enough into the point cloud nodes to find
701 // points which are inside the tolerance.
702 // "4" is a magic number here, based purely on what "looks good" in the profile results!
703 if ( toleranceInPixels / 4 < maximumErrorPixels )
704 maximumErrorPixels = toleranceInPixels / 4;
705
706 double maxErrorInLayerCrs;
707
708 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
709 if ( mapUnitsPerPixel < 0.0 )
710 {
711 QgsDebugError( u"Invalid map units per pixel ratio"_s );
712 return false;
713 }
714
715 QgsGeos originalCurveGeos( sourceCurve );
716 originalCurveGeos.prepareGeometry();
717
718 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, context.elevationRange(), maxErrorInLayerCrs ) || mGatheredPoints.empty() )
719 {
720 mResults = nullptr;
721 return false;
722 }
723
724
725 mResults = std::make_unique< QgsPointCloudLayerProfileResults >();
726 mResults->copyPropertiesFromGenerator( this );
727 mResults->mMaxErrorInLayerCoordinates = maxErrorInLayerCrs;
728
729 // convert x/y values back to distance/height values
730 QString lastError;
731 const QgsPointCloudLayerProfileResults::PointResult *pointData = mGatheredPoints.constData();
732 const int size = mGatheredPoints.size();
733 mResults->results.resize( size );
734 QgsPointCloudLayerProfileResults::PointResult *destData = mResults->results.data();
735 for ( int i = 0; i < size; ++i, ++pointData, ++destData )
736 {
737 if ( mFeedback->isCanceled() )
738 return false;
739
740 *destData = *pointData;
741 destData->distanceAlongCurve = startDistanceOffset + originalCurveGeos.lineLocatePoint( destData->x, destData->y, &lastError );
742 if ( mOpacityByDistanceEffect ) // don't calculate this if we don't need it
743 destData->distanceFromCurve = originalCurveGeos.distance( destData->x, destData->y );
744
745 mResults->minZ = std::min( destData->z, mResults->minZ );
746 mResults->maxZ = std::max( destData->z, mResults->maxZ );
747 }
748 mResults->finalize( mFeedback.get() );
749
750 return true;
751}
752
757
762
763void QgsPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
764{
765 const char *ptr = block->data();
766 int count = block->pointCount();
767
768 const QgsPointCloudAttributeCollection request = block->attributes();
769
770 const std::size_t recordSize = request.pointRecordSize();
771
772 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
773 int xOffset = 0, yOffset = 0, zOffset = 0;
774 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
775 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
776 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
777
778 bool useRenderer = false;
780 {
781 useRenderer = mPreparedRendererData->prepareBlock( block );
782 }
783
784 QColor color;
785 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
786 for ( int i = 0; i < count; ++i )
787 {
788 if ( mFeedback->isCanceled() )
789 {
790 break;
791 }
792
793 QgsPointCloudLayerProfileResults::PointResult res;
794 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), res.x, res.y, res.z );
795
796 res.z = res.z * mZScale + mZOffset;
797 if ( !zRange.contains( res.z ) )
798 continue;
799
800 if ( useRenderer )
801 {
802 color = mPreparedRendererData->pointColor( block, i, res.z );
803 if ( !color.isValid() )
804 continue;
805
806 res.color = color.rgba();
807 }
808 else
809 {
810 res.color = mPointColor.rgba();
811 }
812
813 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x, res.y ) )
814 {
815 if ( reproject )
816 {
817 try
818 {
819 mLayerToTargetTransform.transformInPlace( res.x, res.y, res.z );
820 }
821 catch ( QgsCsException & )
822 {
823 continue;
824 }
825 }
826
827 mGatheredPoints.append( res );
828 }
829 }
830}
831
834{
835 mSymbology = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileSymbology();
836 mElevationLimit = qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->elevationLimit();
837
838 mLineSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileLineSymbol()->clone() );
839 mFillSymbol.reset( qgis::down_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->profileFillSymbol()->clone() );
840}
841
842void QgsTriangulatedPointCloudLayerProfileGenerator::visitBlock( const QgsPointCloudBlock *block, const QgsDoubleRange &zRange )
843{
844 const char *ptr = block->data();
845 int count = block->pointCount();
846
847 const QgsPointCloudAttributeCollection request = block->attributes();
848
849 const std::size_t recordSize = request.pointRecordSize();
850
851 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
852 int xOffset = 0, yOffset = 0, zOffset = 0;
853 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
854 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
855 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
856
857 bool useRenderer = false;
859 {
860 useRenderer = mPreparedRendererData->prepareBlock( block );
861 }
862
863 QColor color;
864 const bool reproject = !mLayerToTargetTransform.isShortCircuited();
865 for ( int i = 0; i < count; ++i )
866 {
867 if ( mFeedback->isCanceled() )
868 {
869 break;
870 }
871
872 double x, y, z;
873 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
874 QgsPoint res( x, y, z * mZScale + mZOffset );
875 if ( !zRange.contains( res.z() ) )
876 continue;
877
878 if ( useRenderer )
879 {
880 color = mPreparedRendererData->pointColor( block, i, res.z() );
881 if ( !color.isValid() )
882 continue;
883 }
884
885 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.x(), res.y() ) )
886 {
887 if ( reproject )
888 {
889 try
890 {
891 double x = res.x(), y = res.y(), z = res.z();
892 mLayerToTargetTransform.transformInPlace( x, y, z );
893 res.setX( x );
894 res.setY( y );
895 res.setZ( z );
896 }
897 catch ( QgsCsException & )
898 {
899 continue;
900 }
901 }
902
903 mGatheredPoints.append( res );
904 }
905 }
906}
907
912
914{
915 return u"triangulatedpointcloud"_s;
916}
917
919
921{
922 mGatheredPoints.clear();
923
924 QgsCurve *sourceCurve = mProfileCurve.get();
925
926 // we need to transform the profile curve and max search extent to the layer's CRS
927 QgsGeos originalCurveGeos( sourceCurve );
928 originalCurveGeos.prepareGeometry();
929
930 if ( mFeedback->isCanceled() )
931 return false;
932
933 double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit );
934 if ( maximumErrorPixels < 0.0 )
935 {
936 QgsDebugError( u"Invalid maximum error in pixels"_s );
937 return false;
938 }
939
940 const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel();
941 if ( mapUnitsPerPixel < 0.0 )
942 {
943 QgsDebugError( u"Invalid map units per pixel ratio"_s );
944 return false;
945 }
946
947 double maxErrorInLayerCrs;
948 if ( !collectData( originalCurveGeos, mapUnitsPerPixel, maximumErrorPixels, QgsDoubleRange(), maxErrorInLayerCrs ) )
949 return false;
950
951 if ( mGatheredPoints.size() < 3 )
952 {
953 mResults = nullptr;
954 return false;
955 }
956
957 mResults = std::make_unique< QgsTriangulatedPointCloudLayerProfileResults >();
958 mResults->copyPropertiesFromGenerator( this );
959
960 std::unique_ptr<delaunator::Delaunator> triangulator;
961 std::vector<double> vertices;
962 std::vector<double> zValues;
963
964 vertices.reserve( mGatheredPoints.size() * 2 );
965 zValues.reserve( mGatheredPoints.size() );
966
967 for ( QgsPoint point : mGatheredPoints )
968 {
969 vertices.push_back( point.x() );
970 vertices.push_back( point.y() );
971 zValues.push_back( point.z() );
972 }
973
974 triangulator.reset( new delaunator::Delaunator( vertices ) );
975 const std::vector<size_t> &triangleIndexes = triangulator->triangles;
976
977 QgsPointSequence profilePoints;
978 QString lastError;
979 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
980 {
981 const size_t idx0 = triangleIndexes[i];
982 const size_t idx1 = triangleIndexes[i + 1];
983 const size_t idx2 = triangleIndexes[i + 2];
984
985 const QgsPoint pt0( vertices[2 * idx0], vertices[2 * idx0 + 1], zValues[idx0] );
986 const QgsPoint pt1( vertices[2 * idx1], vertices[2 * idx1 + 1], zValues[idx1] );
987 const QgsPoint pt2( vertices[2 * idx2], vertices[2 * idx2 + 1], zValues[idx2] );
988
989 const QgsTriangle triangle( pt0, pt1, pt2 );
990
991 QgsGeos geosPoly( &triangle );
992 geosPoly.prepareGeometry();
993
994 std::unique_ptr<QgsAbstractGeometry> intersectingGeom( geosPoly.intersection( sourceCurve ) );
995 if ( !intersectingGeom )
996 continue;
997
998 // probably not needed, but we may end up with something else, better to check
999 if ( QgsWkbTypes::flatType( intersectingGeom->wkbType() ) != Qgis::WkbType::LineString )
1000 continue;
1001
1002 const QgsLineString *linestring = qgsgeometry_cast<const QgsLineString *>( intersectingGeom.get() );
1003 for ( const QgsPoint &p : { linestring->startPoint(), linestring->endPoint() } )
1004 {
1005 const double z = QgsGeometryUtils::interpolateZ( pt0, pt1, pt2, p.x(), p.y() );
1006 if ( std::isnan( z ) )
1007 continue;
1008
1009 const double distanceAlong = originalCurveGeos.lineLocatePoint( p, &lastError );
1010 if ( std::isnan( distanceAlong ) )
1011 continue;
1012
1013 QgsPoint profilePoint( p.x(), p.y(), z, distanceAlong );
1014 profilePoints.append( profilePoint );
1015 }
1016
1017 if ( mFeedback->isCanceled() )
1018 return false;
1019 }
1020
1021 std::sort( profilePoints.begin(), profilePoints.end(),
1022 []( const QgsPoint & a, const QgsPoint & b ) { return a.m() < b.m(); } );
1023
1024 if ( mResults )
1025 {
1026 for ( const QgsPoint &pp : std::as_const( profilePoints ) )
1027 {
1028 const double z = pp.z();
1029 const double distanceAlong = pp.m();
1030
1031 mResults->minZ = std::min( z, mResults->minZ );
1032 mResults->maxZ = std::max( z, mResults->maxZ );
1033 mResults->mDistanceToHeightMap.insert( distanceAlong, z );
1034 mResults->mRawPoints.append( pp );
1035 }
1036 }
1037
1038 return true;
1039}
1040
1045
1050
1052{
1053 return u"triangulatedpointcloud"_s;
1054}
1055
1056QVector<QgsProfileIdentifyResults> QgsTriangulatedPointCloudLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
1057{
1058 const QVector<QgsProfileIdentifyResults> noLayerResults = QgsAbstractProfileSurfaceResults::identify( point, context );
1059
1060 // we have to make a new list, with the correct layer reference set
1061 QVector<QgsProfileIdentifyResults> res;
1062 res.reserve( noLayerResults.size() );
1063 for ( const QgsProfileIdentifyResults &result : noLayerResults )
1064 {
1065 res.append( QgsProfileIdentifyResults( mLayer, result.results() ) );
1066 }
1067 return res;
1068}
1069
1071{
1072 const QgsTriangulatedPointCloudLayerProfileGenerator *pcGenerator = qgis::down_cast< const QgsTriangulatedPointCloudLayerProfileGenerator * >( generator );
1073
1074 mLayer = pcGenerator->mLayer;
1075 mLayerId = pcGenerator->mId;
1076 mCurveCrs = pcGenerator->mTargetCrs;
1077 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
1078 mTolerance = pcGenerator->mTolerance;
1079
1080 mLineSymbol.reset( pcGenerator->mLineSymbol->clone() );
1081 mFillSymbol.reset( pcGenerator->mFillSymbol->clone() );
1082 symbology = pcGenerator->mSymbology;
1083 mElevationLimit = pcGenerator->mElevationLimit;
1084
1085 mZOffset = pcGenerator->mZOffset;
1086 mZScale = pcGenerator->mZScale;
1087}
1088
@ RespectsMaximumErrorMapUnit
Generated profile respects the QgsProfileGenerationContext::maximumErrorMapUnits() property.
Definition qgis.h:4313
@ RespectsDistanceRange
Generated profile respects the QgsProfileGenerationContext::distanceRange() property.
Definition qgis.h:4314
@ Circle
Renders points as circles.
Definition qgis.h:4342
@ Square
Renders points as squares.
Definition qgis.h:4341
QFlags< ProfileGeneratorFlag > ProfileGeneratorFlags
Definition qgis.h:4318
@ Round
Use rounded joins.
Definition qgis.h:2180
@ MapUnits
Map units.
Definition qgis.h:5292
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2168
@ Local
Local means the source is a local file on the machine.
Definition qgis.h:6376
@ Remote
Remote means it's loaded through a protocol like HTTP.
Definition qgis.h:6377
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4327
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4329
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4328
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4330
@ LineString
LineString.
Definition qgis.h:283
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
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:261
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:381
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:254
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:236
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:290
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:55
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:141
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const override
Calculate the intersection of this and geom.
Definition qgsgeos.cpp:316
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition qgsgeos.cpp:2089
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:580
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition qgsgeos.cpp:288
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:3181
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())
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:179
bool contains(const QgsRange< T > &other) const
Returns true if this range contains another range.
Definition qgsrange.h:140
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:81
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:88
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:114
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