QGIS API Documentation 3.99.0-Master (d270888f95f)
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#include <QString>
29
30using namespace Qt::StringLiterals;
31
32//
33// QgsAbstractProfileSurfaceResults
34//
35
37
42
47
52
54{
55 QVector<QgsGeometry> res;
56 res.reserve( mRawPoints.size() );
57 for ( const QgsPoint &point : mRawPoints )
58 res.append( QgsGeometry( point.clone() ) );
59
60 return res;
61}
62
63QVector<QgsAbstractProfileResults::Feature> QgsAbstractProfileSurfaceResults::asFeatures( Qgis::ProfileExportType type, QgsFeedback *feedback ) const
64{
65 QVector< QgsAbstractProfileResults::Feature > res;
66 res.reserve( 1 );
67
68 QVector< double > currentLineX;
69 QVector< double > currentLineY;
70 QVector< double > currentLineZ;
71
72 switch ( type )
73 {
75 {
76 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
77 {
78 if ( feedback && feedback->isCanceled() )
79 break;
80
81
82 if ( std::isnan( pointIt.value() ) )
83 {
84 if ( currentLineX.length() > 1 )
85 {
88 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY, currentLineZ ) );
89 res << f;
90 }
91 currentLineX.clear();
92 currentLineY.clear();
93 currentLineZ.clear();
94 continue;
95 }
96
97 std::unique_ptr< QgsPoint > curvePoint( mProfileCurve->interpolatePoint( pointIt.key() ) );
98 currentLineX << curvePoint->x();
99 currentLineY << curvePoint->y();
100 currentLineZ << pointIt.value();
101 }
102
103 if ( currentLineX.length() > 1 )
104 {
107 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY, currentLineZ ) );
108 res << f;
109 }
110 break;
111 }
112
114 {
115 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
116 {
117 if ( feedback && feedback->isCanceled() )
118 break;
119
120 if ( std::isnan( pointIt.value() ) )
121 {
122 if ( currentLineX.length() > 1 )
123 {
126 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY ) );
127 res << f;
128 }
129 currentLineX.clear();
130 currentLineY.clear();
131 continue;
132 }
133
134 currentLineX << pointIt.key();
135 currentLineY << pointIt.value();
136 }
137 if ( currentLineX.length() > 1 )
138 {
141 f.geometry = QgsGeometry( std::make_unique< QgsLineString >( currentLineX, currentLineY ) );
142 res << f;
143 }
144 break;
145 }
146
148 {
149 res.reserve( mDistanceToHeightMap.size() );
150 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
151 {
152 if ( feedback && feedback->isCanceled() )
153 break;
154
157 f.attributes =
158 {
159 { u"distance"_s, pointIt.key() },
160 { u"elevation"_s, pointIt.value() }
161 };
162 std::unique_ptr< QgsPoint> point( mProfileCurve->interpolatePoint( pointIt.key() ) );
163 if ( point->is3D() )
164 point->setZ( pointIt.value() );
165 else
166 point->addZValue( pointIt.value() );
167 f.geometry = QgsGeometry( std::move( point ) );
168 res << f;
169 }
170 break;
171 }
172 }
173
174 return res;
175}
176
178{
179 // TODO -- consider an index if performance is an issue
181
182 double prevDistance = std::numeric_limits< double >::max();
183 double prevElevation = 0;
184 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
185 {
186 // find segment which corresponds to the given distance along curve
187 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
188 {
189 const double dx = it.key() - prevDistance;
190 const double dy = it.value() - prevElevation;
191 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
192
193 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
194 return QgsProfileSnapResult();
195
196 result.snappedPoint = QgsProfilePoint( point.distance(), snappedZ );
197 break;
198 }
199
200 prevDistance = it.key();
201 prevElevation = it.value();
202 }
203 return result;
204}
205
206QVector<QgsProfileIdentifyResults> QgsAbstractProfileSurfaceResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
207{
208 // TODO -- consider an index if performance is an issue
209 std::optional< QgsProfileIdentifyResults > result;
210
211 double prevDistance = std::numeric_limits< double >::max();
212 double prevElevation = 0;
213 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
214 {
215 // find segment which corresponds to the given distance along curve
216 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
217 {
218 const double dx = it.key() - prevDistance;
219 const double dy = it.value() - prevElevation;
220 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
221
222 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
223 return {};
224
225 result = QgsProfileIdentifyResults( nullptr,
226 {
227 QVariantMap(
228 {
229 {u"distance"_s, point.distance() },
230 {u"elevation"_s, snappedZ }
231 } )
232 } );
233 break;
234 }
235
236 prevDistance = it.key();
237 prevElevation = it.value();
238 }
239 if ( result.has_value() )
240 return {*result};
241 else
242 return {};
243}
244
246{
247 QPainter *painter = context.renderContext().painter();
248 if ( !painter )
249 return;
250
251 const QgsScopedQPainterState painterState( painter );
252
253 painter->setBrush( Qt::NoBrush );
254 painter->setPen( Qt::NoPen );
255
256 const double minDistance = context.distanceRange().lower();
257 const double maxDistance = context.distanceRange().upper();
258 double minZ = context.elevationRange().lower();
259 double maxZ = context.elevationRange().upper();
260
261 const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
262 QPainterPath clipPath;
263 clipPath.addPolygon( context.worldTransform().map( visibleRegion ) );
264 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
265
266 switch ( symbology )
267 {
269 mLineSymbol->startRender( context.renderContext() );
270 break;
272 mFillSymbol->startRender( context.renderContext() );
273 if ( !std::isnan( mElevationLimit ) )
274 {
275 double dataLimit = std::numeric_limits< double >::max();
276 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
277 {
278 if ( !std::isnan( pointIt.value() ) )
279 {
280 dataLimit = std::min( pointIt.value(), dataLimit );
281 }
282 }
283 if ( dataLimit > mElevationLimit )
284 minZ = std::max( minZ, mElevationLimit );
285 }
286 break;
288 mFillSymbol->startRender( context.renderContext() );
289 if ( !std::isnan( mElevationLimit ) )
290 {
291 double dataLimit = std::numeric_limits< double >::lowest();
292 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
293 {
294 if ( !std::isnan( pointIt.value() ) )
295 {
296 dataLimit = std::max( pointIt.value(), dataLimit );
297 }
298 }
299 if ( dataLimit < mElevationLimit )
300 maxZ = std::min( maxZ, mElevationLimit );
301 }
302 break;
303 }
304
305 auto checkLine = [this]( QPolygonF & currentLine, QgsProfileRenderContext & context, double minZ, double maxZ,
306 double prevDistance, double currentPartStartDistance )
307 {
308 if ( currentLine.length() > 1 )
309 {
310 switch ( symbology )
311 {
313 mLineSymbol->renderPolyline( currentLine, nullptr, context.renderContext() );
314 break;
316 currentLine.append( context.worldTransform().map( QPointF( prevDistance, minZ ) ) );
317 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, minZ ) ) );
318 currentLine.append( currentLine.at( 0 ) );
319 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
320 break;
322 currentLine.append( context.worldTransform().map( QPointF( prevDistance, maxZ ) ) );
323 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, maxZ ) ) );
324 currentLine.append( currentLine.at( 0 ) );
325 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
326 break;
327 }
328 }
329 };
330
331 QPolygonF currentLine;
332 double prevDistance = std::numeric_limits< double >::quiet_NaN();
333 double currentPartStartDistance = 0;
334 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
335 {
336 if ( currentLine.empty() ) // new part
337 {
338 if ( std::isnan( pointIt.value() ) ) // skip emptiness
339 continue;
340 currentPartStartDistance = pointIt.key();
341 }
342
343 if ( std::isnan( pointIt.value() ) )
344 {
345 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
346 currentLine.clear();
347 }
348 else
349 {
350 currentLine.append( context.worldTransform().map( QPointF( pointIt.key(), pointIt.value() ) ) );
351 prevDistance = pointIt.key();
352 }
353 }
354
355 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
356
357 switch ( symbology )
358 {
360 mLineSymbol->stopRender( context.renderContext() );
361 break;
364 mFillSymbol->stopRender( context.renderContext() );
365 break;
366 }
367}
368
369
371{
372 const QgsAbstractProfileSurfaceGenerator *surfaceGenerator = qgis::down_cast< const QgsAbstractProfileSurfaceGenerator * >( generator );
373
374 mLineSymbol.reset( surfaceGenerator->lineSymbol()->clone() );
375 mFillSymbol.reset( surfaceGenerator->fillSymbol()->clone() );
376 symbology = surfaceGenerator->symbology();
377 mElevationLimit = surfaceGenerator->elevationLimit();
378
379 mProfileCurve.reset( surfaceGenerator->mProfileCurve->clone() );
380}
381
382//
383// QgsAbstractProfileSurfaceGenerator
384//
385
387 : mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
388{
389
390}
391
393
398
403
408
413
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4292
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4294
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4293
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4295
ProfileSurfaceSymbology
Surface symbology type for elevation profile plots.
Definition qgis.h:4252
@ Line
The elevation surface will be rendered using a line symbol.
Definition qgis.h:4253
@ FillBelow
The elevation surface will be rendered using a fill symbol below the surface level.
Definition qgis.h:4254
@ FillAbove
The elevation surface will be rendered using a fill symbol above the surface level.
Definition qgis.h:4255
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:236
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
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:53
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:81
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:88
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.