QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsabstractprofilesurfacegenerator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsabstractprofilegenerator.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
19#include <optional>
20
21#include "qgsfillsymbol.h"
22#include "qgslinestring.h"
23#include "qgslinesymbol.h"
24#include "qgsprofilerequest.h"
25#include "qgsprofilesnapping.h"
26
27#include <QPainterPath>
28
29//
30// QgsAbstractProfileSurfaceResults
31//
32
34
39
44
49
51{
52 QVector<QgsGeometry> res;
53 res.reserve( mRawPoints.size() );
54 for ( const QgsPoint &point : mRawPoints )
55 res.append( QgsGeometry( point.clone() ) );
56
57 return res;
58}
59
60QVector<QgsAbstractProfileResults::Feature> QgsAbstractProfileSurfaceResults::asFeatures( Qgis::ProfileExportType type, QgsFeedback *feedback ) const
61{
62 QVector< QgsAbstractProfileResults::Feature > res;
63 res.reserve( 1 );
64
65 QVector< double > currentLineX;
66 QVector< double > currentLineY;
67 QVector< double > currentLineZ;
68
69 switch ( type )
70 {
72 {
73 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
74 {
75 if ( feedback && feedback->isCanceled() )
76 break;
77
78
79 if ( std::isnan( pointIt.value() ) )
80 {
81 if ( currentLineX.length() > 1 )
82 {
85 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY, currentLineZ ) );
86 res << f;
87 }
88 currentLineX.clear();
89 currentLineY.clear();
90 currentLineZ.clear();
91 continue;
92 }
93
94 std::unique_ptr< QgsPoint > curvePoint( mProfileCurve->interpolatePoint( pointIt.key() ) );
95 currentLineX << curvePoint->x();
96 currentLineY << curvePoint->y();
97 currentLineZ << pointIt.value();
98 }
99
100 if ( currentLineX.length() > 1 )
101 {
104 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY, currentLineZ ) );
105 res << f;
106 }
107 break;
108 }
109
111 {
112 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
113 {
114 if ( feedback && feedback->isCanceled() )
115 break;
116
117 if ( std::isnan( pointIt.value() ) )
118 {
119 if ( currentLineX.length() > 1 )
120 {
123 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY ) );
124 res << f;
125 }
126 currentLineX.clear();
127 currentLineY.clear();
128 continue;
129 }
130
131 currentLineX << pointIt.key();
132 currentLineY << pointIt.value();
133 }
134 if ( currentLineX.length() > 1 )
135 {
138 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY ) );
139 res << f;
140 }
141 break;
142 }
143
145 {
146 res.reserve( mDistanceToHeightMap.size() );
147 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
148 {
149 if ( feedback && feedback->isCanceled() )
150 break;
151
154 f.attributes =
155 {
156 { QStringLiteral( "distance" ), pointIt.key() },
157 { QStringLiteral( "elevation" ), pointIt.value() }
158 };
159 std::unique_ptr< QgsPoint> point( mProfileCurve->interpolatePoint( pointIt.key() ) );
160 if ( point->is3D() )
161 point->setZ( pointIt.value() );
162 else
163 point->addZValue( pointIt.value() );
164 f.geometry = QgsGeometry( std::move( point ) );
165 res << f;
166 }
167 break;
168 }
169 }
170
171 return res;
172}
173
175{
176 // TODO -- consider an index if performance is an issue
178
179 double prevDistance = std::numeric_limits< double >::max();
180 double prevElevation = 0;
181 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
182 {
183 // find segment which corresponds to the given distance along curve
184 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
185 {
186 const double dx = it.key() - prevDistance;
187 const double dy = it.value() - prevElevation;
188 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
189
190 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
191 return QgsProfileSnapResult();
192
193 result.snappedPoint = QgsProfilePoint( point.distance(), snappedZ );
194 break;
195 }
196
197 prevDistance = it.key();
198 prevElevation = it.value();
199 }
200 return result;
201}
202
203QVector<QgsProfileIdentifyResults> QgsAbstractProfileSurfaceResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
204{
205 // TODO -- consider an index if performance is an issue
206 std::optional< QgsProfileIdentifyResults > result;
207
208 double prevDistance = std::numeric_limits< double >::max();
209 double prevElevation = 0;
210 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
211 {
212 // find segment which corresponds to the given distance along curve
213 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
214 {
215 const double dx = it.key() - prevDistance;
216 const double dy = it.value() - prevElevation;
217 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
218
219 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
220 return {};
221
222 result = QgsProfileIdentifyResults( nullptr,
223 {
224 QVariantMap(
225 {
226 {QStringLiteral( "distance" ), point.distance() },
227 {QStringLiteral( "elevation" ), snappedZ }
228 } )
229 } );
230 break;
231 }
232
233 prevDistance = it.key();
234 prevElevation = it.value();
235 }
236 if ( result.has_value() )
237 return {*result};
238 else
239 return {};
240}
241
243{
244 QPainter *painter = context.renderContext().painter();
245 if ( !painter )
246 return;
247
248 const QgsScopedQPainterState painterState( painter );
249
250 painter->setBrush( Qt::NoBrush );
251 painter->setPen( Qt::NoPen );
252
253 const double minDistance = context.distanceRange().lower();
254 const double maxDistance = context.distanceRange().upper();
255 double minZ = context.elevationRange().lower();
256 double maxZ = context.elevationRange().upper();
257
258 const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
259 QPainterPath clipPath;
260 clipPath.addPolygon( context.worldTransform().map( visibleRegion ) );
261 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
262
263 switch ( symbology )
264 {
266 mLineSymbol->startRender( context.renderContext() );
267 break;
269 mFillSymbol->startRender( context.renderContext() );
270 if ( !std::isnan( mElevationLimit ) )
271 {
272 double dataLimit = std::numeric_limits< double >::max();
273 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
274 {
275 if ( !std::isnan( pointIt.value() ) )
276 {
277 dataLimit = std::min( pointIt.value(), dataLimit );
278 }
279 }
280 if ( dataLimit > mElevationLimit )
281 minZ = std::max( minZ, mElevationLimit );
282 }
283 break;
285 mFillSymbol->startRender( context.renderContext() );
286 if ( !std::isnan( mElevationLimit ) )
287 {
288 double dataLimit = std::numeric_limits< double >::lowest();
289 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
290 {
291 if ( !std::isnan( pointIt.value() ) )
292 {
293 dataLimit = std::max( pointIt.value(), dataLimit );
294 }
295 }
296 if ( dataLimit < mElevationLimit )
297 maxZ = std::min( maxZ, mElevationLimit );
298 }
299 break;
300 }
301
302 auto checkLine = [this]( QPolygonF & currentLine, QgsProfileRenderContext & context, double minZ, double maxZ,
303 double prevDistance, double currentPartStartDistance )
304 {
305 if ( currentLine.length() > 1 )
306 {
307 switch ( symbology )
308 {
310 mLineSymbol->renderPolyline( currentLine, nullptr, context.renderContext() );
311 break;
313 currentLine.append( context.worldTransform().map( QPointF( prevDistance, minZ ) ) );
314 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, minZ ) ) );
315 currentLine.append( currentLine.at( 0 ) );
316 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
317 break;
319 currentLine.append( context.worldTransform().map( QPointF( prevDistance, maxZ ) ) );
320 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, maxZ ) ) );
321 currentLine.append( currentLine.at( 0 ) );
322 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
323 break;
324 }
325 }
326 };
327
328 QPolygonF currentLine;
329 double prevDistance = std::numeric_limits< double >::quiet_NaN();
330 double currentPartStartDistance = 0;
331 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
332 {
333 if ( currentLine.empty() ) // new part
334 {
335 if ( std::isnan( pointIt.value() ) ) // skip emptiness
336 continue;
337 currentPartStartDistance = pointIt.key();
338 }
339
340 if ( std::isnan( pointIt.value() ) )
341 {
342 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
343 currentLine.clear();
344 }
345 else
346 {
347 currentLine.append( context.worldTransform().map( QPointF( pointIt.key(), pointIt.value() ) ) );
348 prevDistance = pointIt.key();
349 }
350 }
351
352 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
353
354 switch ( symbology )
355 {
357 mLineSymbol->stopRender( context.renderContext() );
358 break;
361 mFillSymbol->stopRender( context.renderContext() );
362 break;
363 }
364}
365
366
368{
369 const QgsAbstractProfileSurfaceGenerator *surfaceGenerator = qgis::down_cast< const QgsAbstractProfileSurfaceGenerator * >( generator );
370
371 mLineSymbol.reset( surfaceGenerator->lineSymbol()->clone() );
372 mFillSymbol.reset( surfaceGenerator->fillSymbol()->clone() );
373 symbology = surfaceGenerator->symbology();
374 mElevationLimit = surfaceGenerator->elevationLimit();
375
376 mProfileCurve.reset( surfaceGenerator->mProfileCurve->clone() );
377}
378
379//
380// QgsAbstractProfileSurfaceGenerator
381//
382
384 : mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
385{
386
387}
388
390
395
400
405
410
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4233
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4235
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4234
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4236
ProfileSurfaceSymbology
Surface symbology type for elevation profile plots.
Definition qgis.h:4193
@ Line
The elevation surface will be rendered using a line symbol.
Definition qgis.h:4194
@ FillBelow
The elevation surface will be rendered using a fill symbol below the surface level.
Definition qgis.h:4195
@ FillAbove
The elevation surface will be rendered using a fill symbol above the surface level.
Definition qgis.h:4196
Abstract base class for objects which generate elevation profiles.
virtual QString type() const =0
Returns the unique string identifier for the results type.
Abstract base class for objects which generate elevation profiles which represent a continuous surfac...
QgsLineSymbol * lineSymbol() const
Returns the line symbol to be used for rendering the results.
Qgis::ProfileSurfaceSymbology symbology() const
Returns the symbology type for rendering the results.
void setElevationLimit(double limit)
Sets the elevation limit, which is used when symbology() is Qgis::ProfileSurfaceSymbology::FillBelow ...
QgsAbstractProfileSurfaceGenerator(const QgsProfileRequest &request)
Constructor for QgsAbstractProfileSurfaceGenerator.
double elevationLimit() const
Returns the elevation limit, which is used when symbology() is Qgis::ProfileSurfaceSymbology::FillBel...
QgsFillSymbol * fillSymbol() const
Returns the fill symbol to be used for rendering the results.
QgsPointSequence sampledPoints() const override
Returns a list of sampled points, with their calculated elevation as the point z value.
void renderResults(QgsProfileRenderContext &context) override
Renders the results to the specified context.
void copyPropertiesFromGenerator(const QgsAbstractProfileGenerator *generator) override
Copies properties from specified generator to the results object.
QVector< QgsAbstractProfileResults::Feature > asFeatures(Qgis::ProfileExportType type, QgsFeedback *feedback=nullptr) const override
Returns a list of features representing the calculated elevation results.
QVector< QgsGeometry > asGeometries() const override
Returns a list of geometries representing the calculated elevation results.
QVector< QgsProfileIdentifyResults > identify(const QgsProfilePoint &point, const QgsProfileIdentifyContext &context) override
Identify results visible at the specified profile point.
QgsProfileSnapResult snapPoint(const QgsProfilePoint &point, const QgsProfileSnapContext &context) override
Snaps a point to the generated elevation profile.
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.
QgsRange which stores a range of double values.
Definition qgsrange.h:233
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:53
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
A geometry is the spatial representation of a feature.
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Encapsulates the context of identifying profile results.
double maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when identifying a continuous elevation surfa...
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 maximumSurfaceElevationDelta
Maximum allowed snapping delta for the elevation values when snapping to a continuous elevation surfa...
Encapsulates results of snapping a profile point.
QgsProfilePoint snappedPoint
Snapped point.
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:78
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:85
QPainter * painter()
Returns the destination QPainter for the render operation.
Scoped object for saving and restoring a QPainter object's state.
QVector< QgsPoint > QgsPointSequence
Encapsulates information about a feature exported from the profile results.
QString layerIdentifier
Identifier for grouping output features.
QVariantMap attributes
Exported attributes.