QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsmeshlayerprofilegenerator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshlayerprofilegenerator.cpp
3 ---------------
4 begin : March 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#include "qgsprofilerequest.h"
19#include "qgscurve.h"
20#include "qgsmeshlayer.h"
22#include "qgsgeos.h"
23#include "qgsterrainprovider.h"
24#include "qgsmeshlayerutils.h"
25#include "qgslinesymbol.h"
26#include "qgsfillsymbol.h"
28#include "qgsprofilesnapping.h"
29#include "qgsprofilepoint.h"
30
31//
32// QgsMeshLayerProfileGenerator
33//
34
36{
37 return QStringLiteral( "mesh" );
38}
39
40QVector<QgsProfileIdentifyResults> QgsMeshLayerProfileResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
41{
42 const QVector<QgsProfileIdentifyResults> noLayerResults = QgsAbstractProfileSurfaceResults::identify( point, context );
43
44 // we have to make a new list, with the correct layer reference set
45 QVector<QgsProfileIdentifyResults> res;
46 res.reserve( noLayerResults.size() );
47 for ( const QgsProfileIdentifyResults &result : noLayerResults )
48 {
49 res.append( QgsProfileIdentifyResults( mLayer, result.results() ) );
50 }
51 return res;
52}
53
54//
55// QgsMeshLayerProfileGenerator
56//
57
60 , mId( layer->id() )
61 , mFeedback( std::make_unique< QgsFeedback >() )
62 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
63 , mSourceCrs( layer->crs() )
64 , mTargetCrs( request.crs() )
65 , mTransformContext( request.transformContext() )
66 , mOffset( layer->elevationProperties()->zOffset() )
67 , mScale( layer->elevationProperties()->zScale() )
68 , mLayer( layer )
69 , mStepDistance( request.stepDistance() )
70{
71 layer->updateTriangularMesh();
72 mTriangularMesh = *layer->triangularMesh();
73
74 mSymbology = qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() )->profileSymbology();
75 mElevationLimit = qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() )->elevationLimit();
76 mLineSymbol.reset( qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() )->profileLineSymbol()->clone() );
77 mFillSymbol.reset( qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() )->profileFillSymbol()->clone() );
78}
79
81{
82 return mId;
83}
84
86
88{
89 if ( !mProfileCurve || mFeedback->isCanceled() )
90 return false;
91
92 // we need to transform the profile curve to the mesh's CRS
93 QgsGeometry transformedCurve( mProfileCurve->clone() );
94 mLayerToTargetTransform = QgsCoordinateTransform( mSourceCrs, mTargetCrs, mTransformContext );
95
96 try
97 {
98 transformedCurve.transform( mLayerToTargetTransform, Qgis::TransformDirection::Reverse );
99 }
100 catch ( QgsCsException & )
101 {
102 QgsDebugError( QStringLiteral( "Error transforming profile line to mesh CRS" ) );
103 return false;
104 }
105
106 if ( mFeedback->isCanceled() )
107 return false;
108
109 mResults = std::make_unique< QgsMeshLayerProfileResults >();
110 mResults->mLayer = mLayer;
111 mResults->mId = mId;
112 mResults->copyPropertiesFromGenerator( this );
113
114 // we don't currently have any method to determine line->mesh intersection points, so for now we just sample at about 100(?) points over the line
115 const double curveLength = transformedCurve.length();
116
117 if ( !std::isnan( mStepDistance ) )
118 transformedCurve = transformedCurve.densifyByDistance( mStepDistance );
119 else
120 transformedCurve = transformedCurve.densifyByDistance( curveLength / 100 );
121
122 if ( mFeedback->isCanceled() )
123 return false;
124
125 for ( auto it = transformedCurve.vertices_begin(); it != transformedCurve.vertices_end(); ++it )
126 {
127 if ( mFeedback->isCanceled() )
128 return false;
129
130 QgsPoint point = ( *it );
131 const double height = heightAt( point.x(), point.y() );
132
133 try
134 {
135 point.transform( mLayerToTargetTransform );
136 }
137 catch ( QgsCsException & )
138 {
139 continue;
140 }
141 mResults->mRawPoints.append( QgsPoint( point.x(), point.y(), height ) );
142 }
143
144 if ( mFeedback->isCanceled() )
145 return false;
146
147 // convert x/y values back to distance/height values
148 QgsGeos originalCurveGeos( mProfileCurve.get() );
149 originalCurveGeos.prepareGeometry();
150 QString lastError;
151 for ( const QgsPoint &pixel : std::as_const( mResults->mRawPoints ) )
152 {
153 if ( mFeedback->isCanceled() )
154 return false;
155
156 const double distance = originalCurveGeos.lineLocatePoint( pixel, &lastError );
157
158 if ( !std::isnan( pixel.z() ) )
159 {
160 mResults->minZ = std::min( pixel.z(), mResults->minZ );
161 mResults->maxZ = std::max( pixel.z(), mResults->maxZ );
162 }
163 mResults->mDistanceToHeightMap.insert( distance, pixel.z() );
164 }
165
166 return true;
167}
168
170{
171 return mResults.release();
172}
173
175{
176 return mFeedback.get();
177}
178
179double QgsMeshLayerProfileGenerator::heightAt( double x, double y )
180{
181 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
182}
183
@ Reverse
Reverse/inverse transform (from destination to source)
Abstract base class for storage of elevation profiles.
Abstract base class for objects which generate elevation profiles which represent a continuous surfac...
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context) override
Identify results visible at the specified profile point.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:98
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition: qgsgeos.cpp:252
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:2906
~QgsMeshLayerProfileGenerator() override
QgsFeedback * feedback() const override
Access to feedback object of the generator (may be nullptr)
QgsMeshLayerProfileGenerator(QgsMeshLayer *layer, const QgsProfileRequest &request)
Constructor for QgsMeshLayerProfileGenerator.
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).
QString sourceId() const override
Returns a unique identifier representing the source of the profile.
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.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:101
void updateTriangularMesh(const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Gets native mesh and updates (creates if it doesn't exist) the base triangular mesh.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Definition: qgspoint.cpp:379
double y
Definition: qgspoint.h:53
Encapsulates the context in which an elevation profile is to be generated.
Encapsulates the context of identifying profile results.
Stores identify results generated by a QgsAbstractProfileResults object.
Encapsulates a point on a distance-elevation profile.
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
#define QgsDebugError(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs