QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 = { { u"distance"_s, pointIt.key() }, { u"elevation"_s, pointIt.value() } };
158 std::unique_ptr< QgsPoint> point( mProfileCurve->interpolatePoint( pointIt.key() ) );
159 if ( point->is3D() )
160 point->setZ( pointIt.value() );
161 else
162 point->addZValue( pointIt.value() );
163 f.geometry = QgsGeometry( std::move( point ) );
164 res << f;
165 }
166 break;
167 }
168 }
169
170 return res;
171}
172
174{
175 // TODO -- consider an index if performance is an issue
177
178 double prevDistance = std::numeric_limits< double >::max();
179 double prevElevation = 0;
180 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
181 {
182 // find segment which corresponds to the given distance along curve
183 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
184 {
185 const double dx = it.key() - prevDistance;
186 const double dy = it.value() - prevElevation;
187 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
188
189 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
190 return QgsProfileSnapResult();
191
192 result.snappedPoint = QgsProfilePoint( point.distance(), snappedZ );
193 break;
194 }
195
196 prevDistance = it.key();
197 prevElevation = it.value();
198 }
199 return result;
200}
201
202QVector<QgsProfileIdentifyResults> QgsAbstractProfileSurfaceResults::identify( const QgsProfilePoint &point, const QgsProfileIdentifyContext &context )
203{
204 // TODO -- consider an index if performance is an issue
205 std::optional< QgsProfileIdentifyResults > result;
206
207 double prevDistance = std::numeric_limits< double >::max();
208 double prevElevation = 0;
209 for ( auto it = mDistanceToHeightMap.constBegin(); it != mDistanceToHeightMap.constEnd(); ++it )
210 {
211 // find segment which corresponds to the given distance along curve
212 if ( it != mDistanceToHeightMap.constBegin() && prevDistance <= point.distance() && it.key() >= point.distance() )
213 {
214 const double dx = it.key() - prevDistance;
215 const double dy = it.value() - prevElevation;
216 const double snappedZ = ( dy / dx ) * ( point.distance() - prevDistance ) + prevElevation;
217
218 if ( std::fabs( point.elevation() - snappedZ ) > context.maximumSurfaceElevationDelta )
219 return {};
220
221 result = QgsProfileIdentifyResults( nullptr, { QVariantMap( { { u"distance"_s, point.distance() }, { u"elevation"_s, snappedZ } } ) } );
222 break;
223 }
224
225 prevDistance = it.key();
226 prevElevation = it.value();
227 }
228 if ( result.has_value() )
229 return { *result };
230 else
231 return {};
232}
233
235{
236 QPainter *painter = context.renderContext().painter();
237 if ( !painter )
238 return;
239
240 const QgsScopedQPainterState painterState( painter );
241
242 painter->setBrush( Qt::NoBrush );
243 painter->setPen( Qt::NoPen );
244
245 const double minDistance = context.distanceRange().lower();
246 const double maxDistance = context.distanceRange().upper();
247 double minZ = context.elevationRange().lower();
248 double maxZ = context.elevationRange().upper();
249
250 const QRectF visibleRegion( minDistance, minZ, maxDistance - minDistance, maxZ - minZ );
251 QPainterPath clipPath;
252 clipPath.addPolygon( context.worldTransform().map( visibleRegion ) );
253 painter->setClipPath( clipPath, Qt::ClipOperation::IntersectClip );
254
255 switch ( symbology )
256 {
258 mLineSymbol->startRender( context.renderContext() );
259 break;
261 mFillSymbol->startRender( context.renderContext() );
262 if ( !std::isnan( mElevationLimit ) )
263 {
264 double dataLimit = std::numeric_limits< double >::max();
265 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
266 {
267 if ( !std::isnan( pointIt.value() ) )
268 {
269 dataLimit = std::min( pointIt.value(), dataLimit );
270 }
271 }
272 if ( dataLimit > mElevationLimit )
273 minZ = std::max( minZ, mElevationLimit );
274 }
275 break;
277 mFillSymbol->startRender( context.renderContext() );
278 if ( !std::isnan( mElevationLimit ) )
279 {
280 double dataLimit = std::numeric_limits< double >::lowest();
281 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
282 {
283 if ( !std::isnan( pointIt.value() ) )
284 {
285 dataLimit = std::max( pointIt.value(), dataLimit );
286 }
287 }
288 if ( dataLimit < mElevationLimit )
289 maxZ = std::min( maxZ, mElevationLimit );
290 }
291 break;
292 }
293
294 auto checkLine = [this]( QPolygonF &currentLine, QgsProfileRenderContext &context, double minZ, double maxZ, double prevDistance, double currentPartStartDistance ) {
295 if ( currentLine.length() > 1 )
296 {
297 switch ( symbology )
298 {
300 mLineSymbol->renderPolyline( currentLine, nullptr, context.renderContext() );
301 break;
303 currentLine.append( context.worldTransform().map( QPointF( prevDistance, minZ ) ) );
304 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, minZ ) ) );
305 currentLine.append( currentLine.at( 0 ) );
306 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
307 break;
309 currentLine.append( context.worldTransform().map( QPointF( prevDistance, maxZ ) ) );
310 currentLine.append( context.worldTransform().map( QPointF( currentPartStartDistance, maxZ ) ) );
311 currentLine.append( currentLine.at( 0 ) );
312 mFillSymbol->renderPolygon( currentLine, nullptr, nullptr, context.renderContext() );
313 break;
314 }
315 }
316 };
317
318 QPolygonF currentLine;
319 double prevDistance = std::numeric_limits< double >::quiet_NaN();
320 double currentPartStartDistance = 0;
321 for ( auto pointIt = mDistanceToHeightMap.constBegin(); pointIt != mDistanceToHeightMap.constEnd(); ++pointIt )
322 {
323 if ( currentLine.empty() ) // new part
324 {
325 if ( std::isnan( pointIt.value() ) ) // skip emptiness
326 continue;
327 currentPartStartDistance = pointIt.key();
328 }
329
330 if ( std::isnan( pointIt.value() ) )
331 {
332 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
333 currentLine.clear();
334 }
335 else
336 {
337 currentLine.append( context.worldTransform().map( QPointF( pointIt.key(), pointIt.value() ) ) );
338 prevDistance = pointIt.key();
339 }
340 }
341
342 checkLine( currentLine, context, minZ, maxZ, prevDistance, currentPartStartDistance );
343
344 switch ( symbology )
345 {
347 mLineSymbol->stopRender( context.renderContext() );
348 break;
351 mFillSymbol->stopRender( context.renderContext() );
352 break;
353 }
354}
355
356
358{
359 const QgsAbstractProfileSurfaceGenerator *surfaceGenerator = qgis::down_cast< const QgsAbstractProfileSurfaceGenerator * >( generator );
360
361 mLineSymbol.reset( surfaceGenerator->lineSymbol()->clone() );
362 mFillSymbol.reset( surfaceGenerator->fillSymbol()->clone() );
363 symbology = surfaceGenerator->symbology();
364 mElevationLimit = surfaceGenerator->elevationLimit();
365
366 mProfileCurve.reset( surfaceGenerator->mProfileCurve->clone() );
367}
368
369//
370// QgsAbstractProfileSurfaceGenerator
371//
372
374 : mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
375{}
376
378
383
388
393
398
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:4374
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
Definition qgis.h:4376
@ Features3D
Export profiles as 3D features, with elevation values stored in exported geometry Z values.
Definition qgis.h:4375
@ DistanceVsElevationTable
Export profiles as a table of sampled distance vs elevation values.
Definition qgis.h:4377
ProfileSurfaceSymbology
Surface symbology type for elevation profile plots.
Definition qgis.h:4322
@ Line
The elevation surface will be rendered using a line symbol.
Definition qgis.h:4323
@ FillBelow
The elevation surface will be rendered using a fill symbol below the surface level.
Definition qgis.h:4324
@ FillAbove
The elevation surface will be rendered using a fill symbol above the surface level.
Definition qgis.h:4325
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:217
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:56
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:79
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:86
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.