46 mPointIndex =
nullptr;
53 const std::size_t size =
results.size();
55 for ( std::size_t i = 0; i < size; ++i, ++pointData )
62 GEOSSTRtree_insert_r( geosctxt, mPointIndex, geosPoint.get(), pointData );
69 return QStringLiteral(
"pointcloud" );
75 QMap< double, double > res;
78 res.insert( point.distanceAlongCurve, point.z );
90 res.append(
QgsPoint( point.x, point.y, point.z ) );
98 QVector< QgsGeometry > res;
109 QVector< QgsAbstractProfileResults::Feature > res;
110 res.reserve(
static_cast< int >(
results.size() ) );
135 f.
geometry =
QgsGeometry( std::make_unique< QgsPoint >( point.distanceAlongCurve, point.z ) );
152 { QStringLiteral(
"distance" ), point.distanceAlongCurve },
153 { QStringLiteral(
"elevation" ), point.z }
178 painter->setBrush( Qt::NoBrush );
179 painter->setPen( Qt::NoPen );
197 const QRectF visibleRegion( minDistance,
minZ, maxDistance - minDistance,
maxZ -
minZ );
198 QPainterPath clipPath;
199 clipPath.addPolygon( context.
worldTransform().map( visibleRegion ) );
200 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
206 QPointF p = context.
worldTransform().map( QPointF( point.distanceAlongCurve, point.z ) );
209 color.setAlphaF( color.alphaF() * ( 1.0 - std::pow( point.distanceFromCurve /
tolerance, 0.5 ) ) );
214 painter->fillRect( QRectF( p.x() - penWidth * 0.5,
215 p.y() - penWidth * 0.5,
216 penWidth, penWidth ), color );
220 painter->setBrush( QBrush( color ) );
221 painter->setPen( Qt::NoPen );
222 painter->drawEllipse( QRectF( p.x() - penWidth * 0.5,
223 p.y() - penWidth * 0.5,
224 penWidth, penWidth ) );
232 QList< const QgsPointCloudLayerProfileResults::PointResult * > *
list;
250 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
251 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, minDistance, minElevation );
252 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, maxDistance, maxElevation );
253 geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
255 QList<const PointResult *> items;
257 callbackData.
list = &items;
258 GEOSSTRtree_query_r( geosctxt, mPointIndex, searchDiagonal.get(),
_GEOSQueryCallback, &callbackData );
262 double bestMatchDistance = std::numeric_limits< double >::max();
264 for (
const PointResult *candidate : std::as_const( items ) )
266 const double distance = std::sqrt( std::pow( candidate->distanceAlongCurve - point.
distance(), 2 )
268 if ( distance < bestMatchDistance )
270 bestMatchDistance = distance;
271 bestMatch = candidate;
292 std::unique_ptr< QgsCurve > substring( mProfileCurve->curveSubstring( distanceRange.
lower(), distanceRange.
upper() ) );
293 QgsGeos substringGeos( substring.get() );
294 std::unique_ptr< QgsAbstractGeometry > searchGeometry( substringGeos.
buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
295 if ( !searchGeometry )
301 searchGeometry->transform( curveToLayerTransform );
309 const QgsDoubleRange providerElevationRange( ( elevationRange.
lower() - mZOffset ) / mZScale, ( elevationRange.
upper() - mZOffset ) / mZScale );
311 const QgsGeometry pointCloudSearchGeometry( std::move( searchGeometry ) );
312 const QVector<QVariantMap> pointAttributes = mLayer->dataProvider()->identify( mMaxErrorInLayerCoordinates, pointCloudSearchGeometry, providerElevationRange );
313 if ( pointAttributes.empty() )
330 mLayer = pcGenerator->mLayer;
331 mLayerId = pcGenerator->mId;
332 mCurveCrs = pcGenerator->mTargetCrs;
333 mProfileCurve.reset( pcGenerator->mProfileCurve->clone() );
334 mTolerance = pcGenerator->mTolerance;
336 mZOffset = pcGenerator->mZOffset;
337 mZScale = pcGenerator->mZScale;
346 , mLayerAttributes( layer->attributes() )
347 , mRenderer( qgis::down_cast<
QgsPointCloudLayerElevationProperties* >( layer->elevationProperties() )->respectLayerColors() && mLayer->renderer() ? mLayer->renderer()->clone() : nullptr )
357 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
358 , mTolerance( request.tolerance() )
359 , mSourceCrs( layer->
crs() )
360 , mTargetCrs( request.
crs() )
361 , mTransformContext( request.transformContext() )
362 , mZOffset( layer->elevationProperties()->zOffset() )
363 , mZScale( layer->elevationProperties()->zScale() )
364 , mStepDistance( request.stepDistance() )
366 if ( mLayer->dataProvider()->index() )
368 mScale = mLayer->dataProvider()->index()->scale();
369 mOffset = mLayer->dataProvider()->index()->offset();
387 mGatheredPoints.clear();
388 if ( !mLayer || !mProfileCurve || mFeedback->isCanceled() )
403 std::unique_ptr< QgsCurve > trimmedCurve;
405 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
407 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
408 sourceCurve = trimmedCurve.get();
412 sourceCurve = mProfileCurve.get();
416 QgsGeos originalCurveGeos( sourceCurve );
418 mSearchGeometryInLayerCrs.reset( originalCurveGeos.
buffer( mTolerance, 8, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Round, 2 ) );
423 mSearchGeometryInLayerCrs->transform( mLayerToTargetTransform, Qgis::TransformDirection::Reverse );
427 QgsDebugError( QStringLiteral(
"Error transforming profile line to layer CRS" ) );
431 if ( mFeedback->isCanceled() )
434 mSearchGeometryInLayerCrsGeometryEngine = std::make_unique< QgsGeos >( mSearchGeometryInLayerCrs.get() );
435 mSearchGeometryInLayerCrsGeometryEngine->prepareGeometry();
436 mMaxSearchExtentInLayerCrs = mSearchGeometryInLayerCrs->boundingBox();
446 if ( toleranceInPixels / 4 < maximumErrorPixels )
447 maximumErrorPixels = toleranceInPixels / 4;
459 QgsDebugError( QStringLiteral(
"Could not transform node extent to curve CRS" ) );
460 rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords;
463 const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.
width() / pc->
span();
466 if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumErrorPixels < 0.0 ) )
471 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel;
472 const QVector<IndexedPointCloudNode> nodes = traverseTree( pc, pc->
root(), maximumErrorPixels, rootErrorPixels, context.
elevationRange() );
474 const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.
width() / pc->
span();
475 const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel;
477 mResults = std::make_unique< QgsPointCloudLayerProfileResults >();
478 mResults->copyPropertiesFromGenerator(
this );
479 mResults->mMaxErrorInLayerCoordinates = maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates;
489 mPreparedRendererData = mRenderer->prepare();
490 if ( mPreparedRendererData )
492 const QSet< QString > rendererAttributes = mPreparedRendererData->usedAttributes();
493 for (
const QString &attribute : std::as_const( rendererAttributes ) )
495 if ( attributes.
indexOf( attribute ) >= 0 )
498 const int layerIndex = mLayerAttributes.
indexOf( attribute );
499 if ( layerIndex < 0 )
501 QgsMessageLog::logMessage( QObject::tr(
"Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr(
"Point Cloud" ) );
505 attributes.
push_back( mLayerAttributes.
at( layerIndex ) );
511 mPreparedRendererData.reset();
518 case QgsPointCloudIndex::AccessType::Local:
523 case QgsPointCloudIndex::AccessType::Remote:
530 if ( mFeedback->isCanceled() )
537 const int size = mGatheredPoints.size();
538 mResults->results.resize( size );
540 for (
int i = 0; i < size; ++i, ++pointData, ++destData )
542 if ( mFeedback->isCanceled() )
545 *destData = *pointData;
547 if ( mOpacityByDistanceEffect )
550 mResults->minZ = std::min( destData->
z, mResults->minZ );
551 mResults->maxZ = std::max( destData->
z, mResults->maxZ );
553 mResults->finalize( mFeedback.get() );
560 return mResults.release();
565 return mFeedback.get();
570 QVector<IndexedPointCloudNode> nodes;
572 if ( mFeedback->isCanceled() )
583 if ( !mMaxSearchExtentInLayerCrs.
intersects( nodeMapExtent ) )
587 if ( !mSearchGeometryInLayerCrsGeometryEngine->intersects( nodeMapGeometry.
constGet() ) )
592 double childrenErrorPixels = nodeErrorPixels / 2.0;
593 if ( childrenErrorPixels < maxErrorPixels )
596 const QList<IndexedPointCloudNode> children = pc->
nodeChildren( n );
599 nodes += traverseTree( pc, nn, maxErrorPixels, childrenErrorPixels, zRange );
610 if ( mFeedback->isCanceled() )
613 std::unique_ptr<QgsPointCloudBlock> block( pc->
nodeData( n, request ) );
618 visitBlock( block.get(), zRange );
632 QVector<QgsPointCloudBlockRequest *> blockRequests;
636 for (
int i = 0; i < nodes.size(); ++i )
641 blockRequests.append( blockRequest );
643 [
this, &nodesDrawn, &loop, &blockRequests, &zRange, nStr, blockRequest ]()
645 blockRequests.removeOne( blockRequest );
648 if ( blockRequests.isEmpty() )
651 std::unique_ptr<QgsPointCloudBlock> block( blockRequest->
block() );
653 blockRequest->deleteLater();
655 if ( mFeedback->isCanceled() )
662 QgsDebugError( QStringLiteral(
"Unable to load node %1, error: %2" ).arg( nStr, blockRequest->
errorStr() ) );
666 visitBlock( block.get(), zRange );
678 delete blockRequest->
block();
679 blockRequest->deleteLater();
687 const char *ptr = block->
data();
695 int xOffset = 0, yOffset = 0, zOffset = 0;
700 bool useRenderer =
false;
701 if ( mPreparedRendererData )
703 useRenderer = mPreparedRendererData->prepareBlock( block );
708 for (
int i = 0; i < count; ++i )
710 if ( mFeedback->isCanceled() )
716 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->
scale(), block->
offset(), res.
x, res.
y, res.
z );
718 res.
z = res.
z * mZScale + mZOffset;
724 color = mPreparedRendererData->pointColor( block, i, res.
z );
725 if ( !color.isValid() )
728 res.
color = color.rgba();
732 res.
color = mPointColor.rgba();
735 if ( mSearchGeometryInLayerCrsGeometryEngine->contains( res.
x, res.
y ) )
749 mGatheredPoints.append( res );
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
@ RespectsMaximumErrorMapUnit
Generated profile respects the QgsProfileGenerationContext::maximumErrorMapUnits() property.
@ RespectsDistanceRange
Generated profile respects the QgsProfileGenerationContext::distanceRange() property.
@ Circle
Renders points as circles.
@ Square
Renders points as squares.
ProfileExportType
Types of export for elevation profiles.
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Abstract base class for objects which generate elevation profiles.
Abstract base class for storage of elevation profiles.
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
Abstract base class for curved geometry type.
QgsRange which stores a range of double values.
bool isInfinite() const
Returns true if the range consists of all possible values.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
void canceled()
Internal routines can connect to this signal if they use event loop.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
Does vector analysis using the geos library and handles import, export, exception handling*.
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
static GEOSContextHandle_t getGEOSHandler()
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...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute & at(int index) const
Returns the attribute at the specified index.
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()
Returns the error message string of the request.
QgsPointCloudBlock * block()
Returns the requested block.
void finished()
Emitted when the request processing has finished.
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.
Represents a indexed point clouds data in octree.
int span() const
Returns the number of points in one direction in a single node.
QgsRectangle nodeMapExtent(const IndexedPointCloudNode &node) const
Returns the extent of a node in map coordinates.
virtual QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a node data block.
virtual QList< IndexedPointCloudNode > nodeChildren(const IndexedPointCloudNode &n) const
Returns all children of node.
virtual AccessType accessType() const =0
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
virtual bool isValid() const =0
Returns whether index is loaded and valid.
virtual QgsPointCloudBlock * nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
IndexedPointCloudNode root()
Returns root node of the index.
QgsDoubleRange nodeZRange(const IndexedPointCloudNode &node) const
Returns the z range of a node.
Point cloud layer specific subclass of QgsMapLayerElevationProperties.
Implementation of QgsAbstractProfileGenerator for point cloud layers.
Qgis::ProfileGeneratorFlags flags() const override
Returns flags which reflect how the profile generator operates.
QgsPointCloudLayerProfileGenerator(QgsPointCloudLayer *layer, const QgsProfileRequest &request)
Constructor for QgsPointCloudLayerProfileGenerator.
~QgsPointCloudLayerProfileGenerator() override
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).
Qgis::PointCloudSymbol pointSymbol
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.
std::vector< PointResult > results
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.
~QgsPointCloudLayerProfileResults() override
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.
bool opacityByDistanceEffect
QgsPointCloudLayerProfileResults()
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.
Qgis::RenderUnit pointSizeUnit
Represents a map layer supporting display of point clouds.
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.
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 SIP_HOLDGIL
Returns the elevation of the point.
double distance() const SIP_HOLDGIL
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
bool overlaps(const QgsRange< T > &other) const
Returns true if this range overlaps another range.
bool contains(const QgsRange< T > &other) const
Returns true if this range contains another range.
T lower() const
Returns the lower bound of the range.
T upper() const
Returns the upper bound of the range.
A rectangle specified with double values.
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
double width() const SIP_HOLDGIL
Returns the width of the 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.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
QVector< QgsPoint > QgsPointSequence
#define QgsDebugError(str)
void _GEOSQueryCallback(void *item, void *userdata)
const QgsCoordinateReferenceSystem & crs
Encapsulates information about a feature exported from the profile results.
QString layerIdentifier
Identifier for grouping output features.
QVariantMap attributes
Exported attributes.
QgsGeometry geometry
Exported geometry.
double distanceAlongCurve
QList< const QgsPointCloudLayerProfileResults::PointResult * > * list